Empty State
Empty data placeholder. Icon + title + description + optional action.
Overview
The Empty State component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.
Why this exists
Empty states are often neglected but are critical onboarding moments. A good empty state explains what the area is for and provides a clear next action.
Mental model
An empty state appears when there is no data to display. It should educate (what goes here) and guide (how to add something).
When to use
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
Default with action
No items yet
Create your first item to get started. You can always come back here to view all items.
Inside a card
Activity
No activity to show
Recent events will appear here once your team starts collaborating.
No search results
No results for “byteion”
Try a different search term, or remove some filters to broaden your results.
Error state
Something went wrong
We couldn't load your data. Check your connection and try again.
All caught up
Inbox zero
You've handled all your messages. Take a break — you've earned it.
import { Button, EmptyState } from '@byteion/react';
export default function EmptyStateDemo() {
return (
<div className="p-6">
<EmptyState
icon={<i className="ti ti-inbox text-3xl" />}
title="No invoices yet"
description="Create your first invoice to start tracking payments."
actions={
<>
<Button variant="outline">Import CSV</Button>
<Button>New invoice</Button>
</>
}
/>
</div>
);
}
<script setup lang="ts">
import { Button, EmptyState } from '@byteion/vue';
</script>
<template>
<div class="p-6">
<EmptyState title="No invoices yet"
description="Create your first invoice to start tracking payments.">
<template #icon><i class="ti ti-inbox text-3xl" /></template>
<template #actions>
<Button variant="outline">Import CSV</Button>
<Button>New invoice</Button>
</template>
</EmptyState>
</div>
</template>
Variants
Each variant communicates a different meaning. Choose based on priority, not aesthetics.
Single default variant.
Sizes
Size should match the importance and context of the component.
- md — md size.
States
This component reacts seamlessly to the following interaction and data states:
- empty — empty state.
- error — Validation failed — red border and error message.
- success — Validation passed — green indicator.
- default — Resting appearance before interaction.
- hover — Pointer is over the element — visual feedback via color or shadow.
- focus — Keyboard or tab focus — shows ring indicator for accessibility.
- active — Currently selected or in use.
- disabled — Non-interactive — grayed out, cursor not-allowed.
Anatomy
Usage guidelines
- Always provide a CTA to create the first item.
- Use friendly, non-technical language.
- Include an illustration or icon.
Do and Don't
Do
- Provide a clear CTA.
- Use friendly language.
- Include an illustration.
Don't
- Do not show a blank page.
- Do not use technical error language.
- Do not omit the CTA.
Accessibility
Title and description must be readable by screen readers. CTA must be a button or link.
Role:status (when shown)
Customization
Icon and color follow design tokens.
Compose with React cva()
Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.
import { cva, cn, type VariantProps } from '@byteion/react';
const myEmptyState = cva('base classes…', {
variants: {
accent: { soft:'border-primary-200', loud:'border-primary-500' },
},
defaultVariants: { accent:'soft' },
});
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myEmptyState>;
Compose with Vue cva()
Use the exportedcva() +cn() utilities inside standard Vue setup blocks.
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';
interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();
const myEmptyState = cva('base classes…', {
variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});
const cls = computed(() => myEmptyState({ accent: props.accent, class: attrs.class as string | undefined }));
</script>
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
No HTML runtime events are declared by this adapter.
React callbacks
No React callback events are declared by this adapter.
Vue emits
No Vue emitted events are declared by this adapter.
Remote / AJAX
This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.
API Reference
Static HTML composition.
HTML Data Attributes
No custom data attributes required.
React Props · EmptyStateProps
Extracted directly fromreact/src/components/EmptyState.tsx.
export interface EmptyStateProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'>,
VariantProps<typeof emptyState> {
/** Icon node (e.g. `<svg viewBox="0 0 24 24" className="text-3xl" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false"><path d="M4 6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
<path d="M4 13h3l3 3h4l3 -3h3" /></svg>`). */
icon?: ReactNode;
/** Visual style for the icon wrapper. */
iconTone?: keyof typeof ICON_WRAP;
/** Icon wrapper size. */
iconSize?: keyof typeof ICON_SIZE;
/** Heading text. */
title?: ReactNode;
/** Body / description text. */
description?: ReactNode;
/** Action area - typically one or two `<Button>` elements. */
actions?: ReactNode;
/** Render additional content below the actions. */
children?: ReactNode;
}
React Variant Schema
Internal variant composition defined viacva().
cva(
'flex flex-col items-center justify-center px-8 text-center',
{
variants: {
variant: {
dashed: 'rounded-2xl border-2 border-dashed border-border bg-muted/40 py-16',
card: 'rounded-xl border border-border bg-card py-16',
error: 'rounded-xl border border-danger-200 bg-danger-50/40 py-12 dark:border-danger-500/20 dark:bg-danger-500/5',
success: 'rounded-xl border border-success-200 bg-success-50/40 py-12 dark:border-success-500/20 dark:bg-success-500/5',
inset: 'py-12', // no border - for use inside cards/panels
},
},
defaultVariants: { variant: 'dashed' },
},
)
Vue Props · EmptyStateProps
Extracted directly fromvue/src/components/EmptyState.vue.
export interface EmptyStateProps {
variant?: 'dashed' | 'card' | 'error' | 'success' | 'inset';
/** Visual style for the icon wrapper. */
iconTone?: EmptyStateIconTone;
/** Icon wrapper size. */
iconSize?: EmptyStateIconSize;
/** Heading text. */
title?: string;
/** Body / description text. */
description?: string;
}
Developer tips
Common mistakes
Production usage tips
data-bi-* attributes instead of styling hooks.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components