Build a responsive React dashboard
Use Grid for mobile-first columns, GridItem for a wider content region, and shadcn cards for the content. CSS handles every breakpoint.
Install the components
Start with a React 19 and TypeScript project configured for Tailwind CSS 4 and shadcn/ui. Register @layout-kit using the installation guide, then add the layout bundle and cards.
npx shadcn@latest add @layout-kit/layout
npx shadcn@latest add cardThe CLI copies source into your project and installs Base UI for the layout primitives. Follow the installation guide for registry setup and aliases. This example needs no charting library, API, router, or client-side state.
Plan the responsive grid
Begin with one column so cards remain readable on a phone. Increase the metric grid to two columns at sm and three at lg. Responsive maps use Tailwind’s default media-query breakpoints.
Complete example
The preview and the source below use the same component. Replace the sample metrics with your data; the layout does not own fetching, loading, or error handling.
Order dashboard
24
18
6
Review the remaining six payments before dispatch.
- Print shipping labels
- Confirm collection time
import { Grid } from "@/components/ui/grid";
import { GridItem } from "@/components/ui/grid-item";
import { Page } from "@/components/ui/page";
import { Stack } from "@/components/ui/stack";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
const metrics = [
{ label: "Open orders", value: "24" },
{ label: "Ready to ship", value: "18" },
{ label: "Awaiting payment", value: "6" },
];
export function ResponsiveDashboard({ titleAs = "h1" }: { titleAs?: "h1" | "h2" }) {
return (
<Page size="xl">
<Page.Header
title="Order dashboard"
titleAs={titleAs}
description="Sample fulfillment overview"
/>
<Page.Body>
<Stack gap={6}>
<Grid cols={{ base: 1, sm: 2, lg: 3 }} gap={4}>
{metrics.map((metric) => (
<Card key={metric.label}>
<CardHeader>
<CardTitle>{metric.label}</CardTitle>
<CardDescription>Current sample snapshot</CardDescription>
</CardHeader>
<CardContent>
<p>{metric.value}</p>
</CardContent>
</Card>
))}
</Grid>
<Grid cols={{ base: 1, lg: 3 }} gap={6} align="start">
<GridItem span={{ base: 1, lg: 2 }}>
<Card>
<CardHeader>
<CardTitle>Fulfillment</CardTitle>
<CardDescription>18 of 24 orders are ready to ship.</CardDescription>
</CardHeader>
<CardContent>
<p>Review the remaining six payments before dispatch.</p>
</CardContent>
</Card>
</GridItem>
<Card>
<CardHeader>
<CardTitle>Next dispatch</CardTitle>
<CardDescription>Warehouse checklist</CardDescription>
</CardHeader>
<CardContent>
<Stack render={<ul />} gap={2}>
<li>Print shipping labels</li>
<li>Confirm collection time</li>
</Stack>
</CardContent>
</Card>
</Grid>
</Stack>
</Page.Body>
</Page>
);
}Check the result
See Grid props, GridItem spans, and the page sidebar guide to extend this screen. Direct Tailwind utilities remain a good fit for a one-off layout that does not need a shared prop vocabulary.