Placeholder
Centered empty-state block — a graphic, a title, a description, and an optional action. Use it for empty lists, "no results" states, offline/error screens, and onboarding prompts.
Basic Usage
import { Button, Icon, Placeholder } from '@multinaire/expo-ui';
function Example() {
return (
<Placeholder
flex={1}
graphic={<Icon icon="Box" size={64} color="neutral" />}
title="Nothing here yet"
description="Items you create will show up on this screen."
action={<Button title="Create item" onPress={handleCreate} />}
/>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
flex | number | — | Flex grow value. Pass 1 to fill the remaining space of the parent and center the content vertically within it |
padding | SpacingProps | — | Inner padding. Accepts a number for uniform padding, or an object for per-side control |
textAlignment | 'left' | 'center' | 'right' | 'center' | Alignment of the title and description, and of the block's content within its own width |
graphic | ReactElement | — | Required. Element rendered above the text — typically an Icon, Photo, or an illustration |
title | TranslationKey | — | Required. Headline text describing the empty or error state |
description | TranslationKey | — | Required. Supporting text explaining the state or what to do next |
action | ReactElement | — | Element rendered below the text — typically a Button offering the primary recovery action |
Examples
Empty List
Render it in place of the list once the data resolves to an empty array.
function Contacts() {
const { data, isLoading } = useContacts();
if (!isLoading && data.length === 0) {
return (
<Placeholder
flex={1}
padding={variables.padding.large}
graphic={<Icon icon="Profile2User" size={64} color="neutral" />}
title="No contacts"
description="Add your first contact to get started."
action={<Button title="Add contact" onPress={openAddContact} />}
/>
);
}
return <ListContainer data={data} renderItem={renderContact} />;
}
No Search Results
An action isn't always needed — leave action off when there's nothing useful to offer.
<Placeholder
flex={1}
graphic={<Icon icon="SearchNormal1" size={64} color="neutral" />}
title="No results"
description="Try a different search term."
/>
Error State
<Placeholder
flex={1}
graphic={<Icon icon="CloseCircle" size={64} color="error" />}
title="Something went wrong"
description="We couldn't load this page. Check your connection and try again."
action={<Button type="secondary" title="Retry" onPress={refetch} />}
/>
Left-Aligned
Pass textAlignment="left" when the placeholder sits inside a form or a left-aligned content column, so its text lines up with the surrounding copy.
<Placeholder
textAlignment="left"
graphic={<Icon icon="DocumentText" size={48} color="neutral" />}
title="No attachments"
description="Files you upload will be listed here."
/>
With an Illustration
graphic accepts any element, so a Photo or a custom SVG works just as well as an Icon.
<Placeholder
flex={1}
graphic={
<Photo
source={require('../assets/empty-inbox.png')}
width={200}
height={160}
/>
}
title="Inbox zero"
description="You're all caught up."
/>
Inside a Card
Without flex, the placeholder shrinks to its content — useful for an empty section inside a larger screen.
<Card padding={variables.padding.large}>
<Placeholder
graphic={<Icon icon="Chart" size={48} color="neutral" />}
title="No data for this period"
description="Pick a different date range to see results."
/>
</Card>
Notes
- Content is capped at
BREAKPOINTS.mobile(640px) wide and centered in its parent, so the text stays readable on tablets and on the web instead of stretching across the full viewport. titleanddescriptionareTranslationKeys — they're passed throughTypography, so string literals are translated automatically via theLocalizationProvider.- The title renders as
subheading/boldand the description astitlein theneutralcolor; both followtextAlignment. - Without
flex, the block sizes to its content. Passflex={1}to have it fill the remaining space of its parent — the usual choice when it replaces a full-screen list. - The graphic is always horizontally centered, whatever
textAlignmentis set to. textAlignmentalso drives the block's ownalignItems:'center'centers,'right'aligns to the end, and anything else ('left','auto','justify') aligns to the start.