Empty State
What a Screen or list shows when there is nothing to show yet: an icon, a title, a description and an action.
Componentfeedback
Style
Light
Dark
Your app is ready.
Inter body text, Inter headings.
smmdlgxl
you@example.com
Continue
a0: Vega, neutral, neutral accent, default radius, Inter.Change PresetInstallation
$
npx nativecn-cli@latest add empty-stateAlso installs: theme, text, icon.
Packages: lucide-react-native@^1.51.0, react-native-svg.
Usage
Empty State demo
An empty inbox with a New message action.
import { Inbox, Plus } from "lucide-react-native";
import { Button } from "@/components/button";
import { EmptyState } from "@/components/empty-state";
/** An empty inbox with an action. */
export default function EmptyStateDemo() {
return (
<EmptyState
icon={Inbox}
title="No messages yet"
description="When someone writes to you, their message will appear here."
>
<Button label="New message" icon={Plus} />
</EmptyState>
);
}Empty State demo
An empty inbox with a New message action.
import { Inbox, Plus } from "lucide-react-native";
import { Button } from "@/components/button";
import { EmptyState } from "@/components/empty-state";
/** An empty inbox with an action. */
export default function EmptyStateDemo() {
return (
<EmptyState
icon={Inbox}
title="No messages yet"
description="When someone writes to you, their message will appear here."
>
<Button label="New message" icon={Plus} />
</EmptyState>
);
}Props
| Prop | Type and description |
|---|---|
icon | Lucide icon component, drawn in a muted circle |
title | string (required), read as a heading |
description | string |
children | the action, usually a Button |
Accessibility
- The title has role heading (the h4 text Variant).
- The icon is decorative and hidden from screen readers.
- The action (usually a Button) brings its own role and name.
Notes
Centre it in the free space (style={{ flex: 1 }}, or a list's ListEmptyComponent). Padding, gap and icon size come from the empty-state.root and empty-state.icon Style Slots.
Source
The Vega Style's code, exactly as add copies it into your app.
{components}/empty-state.tsx
import type { LucideIcon } from "lucide-react-native";
import type { ReactNode, Ref } from "react";
import { View, type ViewProps } from "react-native";
import { Icon } from "@/components/icon";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
export type EmptyStateProps = Omit<ViewProps, "children"> & {
/** A Lucide icon component, drawn in a soft circle above the title. */
icon?: LucideIcon;
/** What is empty, e.g. "No messages yet". Read as a heading. */
title: string;
/** What the user can do about it. */
description?: string;
/** The action, usually a Button. */
children?: ReactNode;
ref?: Ref<View>;
};
const useStyles = createStyles((t) => ({
root: { ...{ padding: t.spacing[8], gap: t.spacing[3] }, alignItems: "center", justifyContent: "center" },
icon: {
...{ width: t.scaleValue(48), height: t.scaleValue(48) },
borderRadius: t.radius.full,
alignItems: "center",
justifyContent: "center",
backgroundColor: t.colors.muted,
},
text: { alignItems: "center", gap: t.spacing[1] },
action: { marginTop: t.spacing[2] },
}));
/**
* What a Screen or list shows when there is nothing to show yet: an icon, a title, a short
* description and an optional action (`children`). The title is a heading. `style` is merged
* last.
*/
export function EmptyState({
icon,
title,
description,
children,
style,
...props
}: EmptyStateProps) {
const styles = useStyles();
return (
<View style={[styles.root, style]} {...props}>
{icon ? (
<View style={styles.icon}>
<Icon icon={icon} size="lg" color="mutedForeground" />
</View>
) : null}
<View style={styles.text}>
<Text variant="h4" align="center">
{title}
</Text>
{description ? (
<Text variant="small" color="mutedForeground" align="center">
{description}
</Text>
) : null}
</View>
{children != null ? <View style={styles.action}>{children}</View> : null}
</View>
);
}{components}/empty-state.tsx
import type { LucideIcon } from "lucide-react-native";
import type { ReactNode, Ref } from "react";
import { View, type ViewProps } from "react-native";
import { Icon } from "@/components/icon";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
export type EmptyStateProps = Omit<ViewProps, "children"> & {
/** A Lucide icon component, drawn in a soft circle above the title. */
icon?: LucideIcon;
/** What is empty, e.g. "No messages yet". Read as a heading. */
title: string;
/** What the user can do about it. */
description?: string;
/** The action, usually a Button. */
children?: ReactNode;
ref?: Ref<View>;
};
const useStyles = createStyles((t) => ({
root: { ...{ padding: t.spacing[6], gap: t.spacing[2] }, alignItems: "center", justifyContent: "center" },
icon: {
...{ width: t.scaleValue(36), height: t.scaleValue(36) },
borderRadius: t.radius.full,
alignItems: "center",
justifyContent: "center",
backgroundColor: t.colors.muted,
},
text: { alignItems: "center", gap: t.spacing[1] },
action: { marginTop: t.spacing[2] },
}));
/**
* What a Screen or list shows when there is nothing to show yet: an icon, a title, a short
* description and an optional action (`children`). The title is a heading. `style` is merged
* last.
*/
export function EmptyState({
icon,
title,
description,
children,
style,
...props
}: EmptyStateProps) {
const styles = useStyles();
return (
<View style={[styles.root, style]} {...props}>
{icon ? (
<View style={styles.icon}>
<Icon icon={icon} size="lg" color="mutedForeground" />
</View>
) : null}
<View style={styles.text}>
<Text variant="h4" align="center">
{title}
</Text>
{description ? (
<Text variant="small" color="mutedForeground" align="center">
{description}
</Text>
) : null}
</View>
{children != null ? <View style={styles.action}>{children}</View> : null}
</View>
);
}
