Skip to content
Guide

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.

React 19Tailwind CSS 4shadcn/ui
01 / Prerequisites

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.

Terminal
npx shadcn@latest add @layout-kit/layout
npx shadcn@latest add card

The 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.

02 / Layout decisions

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.

  • cols={{ base: 1, sm: 2, lg: 3 }} gives the metrics one, two, and three columns at 0, 640px, and 1024px with the default root font size.
  • The second Grid stays at one column until lg. Its primary region spans two of three columns only at lg.
  • GridItem’s base span stays at one. A span of two inside a one-column grid would create an unintended implicit track.
  • Stack owns the space between the metric grid and the detail grid; cards do not need bottom margins.
03 / Copy and adapt

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

Sample fulfillment overview
Open orders
Current sample snapshot

24

Ready to ship
Current sample snapshot

18

Awaiting payment
Current sample snapshot

6

Fulfillment
18 of 24 orders are ready to ship.

Review the remaining six payments before dispatch.

Next dispatch
Warehouse checklist
  • Print shipping labels
  • Confirm collection time
components/order-dashboard.tsx
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>
  );
}
04 / Verification

Check the result

  • Resize below sm, between sm and lg, and above lg. Check both column counts and the primary region’s span.
  • Try long metric labels and realistic localized values. min-w-0 allows shrinking but does not truncate text or make a wide table scroll automatically.
  • Check at 200% zoom. Charts and tables you add later need their own accessible names and overflow treatment.
  • Keep reading order meaningful: this example never rearranges the DOM to match a visual position.

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.