Skip to content
React NativeBeta

Skeleton & Empty

Loading placeholders that pulse or shimmer, and empty states.

Beta. React Native support is in beta. APIs may change or break between prereleases (npm tag native).

On this page
Web preview — real device may differ
tsx
import { Text, View } from "react-native";
import { Button } from "@/component-lib/button";
import { useKitTheme } from "@/component-lib/shared/native";
import { Empty, Skeleton } from "@/component-lib/skeleton";
import { Section } from "./layout"; // Section and Row: page layout for the demo
 
export function SkeletonDemo() {
  const { tokens } = useKitTheme();
  return (
    <>
      <Section title="Skeleton · pulse">
        <View style={{ flexDirection: "row", gap: 12, alignItems: "center" }}>
          <Skeleton variant="circle" width={48} />
          <View style={{ flex: 1 }}>
            <Skeleton lines={2} lastLineWidth={45} />
          </View>
        </View>
        <Skeleton variant="rect" height={120} label="Loading the chart" />
      </Section>
 
      <Section title="Skeleton · wave">
        <Skeleton lines={4} animation="wave" />
        <View style={{ flexDirection: "row", gap: 12 }}>
          <Skeleton variant="rect" width="48%" height={90} radius={16} animation="wave" />
          <Skeleton variant="rect" width="48%" height={90} radius={16} animation="wave" />
        </View>
      </Section>
 
      <Section title="Skeleton · none">
        <Skeleton lines={3} animation="none" />
      </Section>
 
      <Section title="Empty">
        <View style={{ borderWidth: 1, borderColor: tokens.border, borderRadius: tokens.radius }}>
          <Empty
            icon={<Text style={{ fontSize: 36 }}>🗂️</Text>}
            title="No invoices yet"
            description="Invoices you create or import show up here. Start with one, or import last year's from a spreadsheet."
            actions={
              <>
                <Button>New invoice</Button>
                <Button variant="outline">Import</Button>
              </>
            }
          />
        </View>
        <View style={{ borderWidth: 1, borderColor: tokens.border, borderRadius: tokens.radius }}>
          <Empty size="sm" title="No results" description="Try a shorter search." />
        </View>
      </Section>
    </>
  );
}

Install

npx @grampro/kit@native add Skeleton

Usage

tsx
import { Skeleton, Empty } from "@/component-lib/skeleton";
 
<Skeleton variant="text" lines={3} />
<Empty title="No invoices yet" description="Create one to get paid." />

Props

SkeletonProps

PropTypeDescription
variantSkeletonVariantDefault text.
linesnumberNumber of bars, for text. Default 1.
lastLineWidthnumberWidth of the last line as a percentage, for text. Default 60.
widthDimensionValueA number is dp; a string is a percentage such as "50%".
heightDimensionValueA number is dp. Left out, each variant uses its own height, which is what keeps a skeleton the size of the thing it stands in for.
radiusnumberCorner radius for rect, in dp.
animationSkeletonAnimationDefault pulse. The device's reduce-motion setting turns any of them off.
labelstringAnnounced while the placeholder is up. Leave it out inside a region that already says it is busy: two announcements are worse than one.
styleStyleProp<ViewStyle>
stylesPartial<{ root: StyleProp<ViewStyle>; line: StyleProp<ViewStyle> }>

EmptyProps

PropTypeDescription
title (required)ReactNodeWhat is missing, in a few words.
descriptionReactNodeWhy it is empty, and what to do about it.
iconReactNodeDecorative: an illustration or icon above the title.
actionsReactNodeThe way out: usually one button, occasionally two.
size"sm" | "md" | "lg"Default md. sm suits an empty panel inside a screen.
styleStyleProp<ViewStyle>
stylesPartial<{ root: StyleProp<ViewStyle>; icon: StyleProp<ViewStyle>; title: StyleProp<Text…

Native and web