Browse docs
Browse docs
The wrapper around every page. Centered, capped at a max-width, padded responsive — the universal "page chrome" pattern. Without Container, every page in your app rewrites mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 until somebody forgets a breakpoint.
import { Container } from '@dashforge/tw';
<Container size="lg" as="main">
<h1>Page content lives here</h1>
</Container>{/* Default — xl (1280px) max-width, responsive padding */}
<Container>
<h1>App content</h1>
</Container>
{/* Documentation-friendly width */}
<Container size="lg">
<article>Markdown article cap at 1024px</article>
</Container>
{/* Full-bleed (no max-width, padding still applies) */}
<Container size="fluid">
Hero section that spans the viewport
</Container>size defaults to 'xl' — comfortable for full app shells with a left nav + main + optional right rail.
size | max-width | Use case |
|---|---|---|
sm | 640px | Sign-in cards, single-column flows |
md | 768px | Settings panels, narrow content |
lg | 1024px | Documentation, marketing, blog |
xl (default) | 1280px | Standard app shells |
2xl | 1536px | Wide dashboards, multi-column views |
fluid | none | Full-bleed sections (padding still applies) |
import { Container, Box, Stack, Typography } from '@dashforge/tw';
<Stack gap={2}>
<Container size="sm">
<Box variant="outlined" p={3} rounded="md">
<Typography variant="caption" color="muted">size="sm" — max-w-screen-sm (640px)</Typography>
</Box>
</Container>
<Container size="md">
<Box variant="outlined" p={3} rounded="md">
<Typography variant="caption" color="muted">size="md" — max-w-screen-md (768px)</Typography>
</Box>
</Container>
<Container size="lg">
<Box variant="outlined" p={3} rounded="md">
<Typography variant="caption" color="muted">size="lg" — max-w-screen-lg (1024px)</Typography>
</Box>
</Container>
</Stack><Container size="sm"> 640px max</Container>
<Container size="md"> 768px max</Container>
<Container size="lg"> 1024px max</Container>
<Container size="xl"> 1280px max — default</Container>
<Container size="2xl"> 1536px max</Container>
<Container size="fluid">no cap, edge to edge</Container>Names match Tailwind's breakpoint vocabulary so the muscle memory carries: size="lg" ↔ max-w-screen-lg.
{/* Default — responsive padding ramp */}
<Container>px-4 sm:px-6 lg:px-8 — the canonical ramp</Container>
{/* No padding — full bleed */}
<Container px={false}>
<img src="hero.jpg" className="w-full" />
</Container>
{/* Custom vertical padding — Container doesn't expose `py`, use sx */}
<Container sx="py-12">section with vertical breathing room</Container>The horizontal padding is opinionated: the px-4 sm:px-6 lg:px-8 ramp is what every well-spaced web app uses (it keeps edges from kissing the viewport on mobile and breathes more on larger screens). When you want full bleed at the page edges, pass px={false} — typical use case is a hero image or video that bleeds to the viewport edge.
centerContent — stack children centered<Container centerContent size="md">
<Typography variant="h1" align="center">Welcome back</Typography>
<Typography variant="body1" color="muted">Sign in to continue</Typography>
<Button color="primary">Continue with email</Button>
</Container>centerContent turns the Container into a flex column with items-center. Useful for marketing pages, sign-in flows, "single artifact" layouts where the whole content stacks centered.
as for semantic shell{/* Primary content region of a page */}
<Container as="main" size="lg">
<Outlet />
</Container>
{/* Named section in a longer layout */}
<Container as="section" size="lg" sx="py-16">
<Typography variant="h2">Features</Typography>
</Container>
{/* Article shell */}
<Container as="article" size="md">
<ArticleHeader />
<ArticleBody />
</Container>Use as="main" for the primary content region — gives you the semantic outline browsers and screen readers expect, plus the Container chrome in one component.
asChild — paint onto an existing elementimport { Outlet } from 'react-router-dom';
<Container asChild size="xl">
<main className="bg-neutral-50 dark:bg-neutral-950">
<Outlet />
</main>
</Container>asChild merges Container's styles onto the child via Radix Slot — useful when the child already has semantic meaning AND its own classes you want preserved. tailwind-merge resolves conflicts (your child classes win where they collide with Container's).
<Container size="lg" as="main" sx="py-12">
<Stack gap={8}>
<Typography variant="h1">Workspace dashboard</Typography>
<Grid container spacing={6}>
<Grid xs={12} md={4}>
<Box variant="outlined" rounded="xl" p={6}>Stats</Box>
</Grid>
<Grid xs={12} md={4}>
<Box variant="outlined" rounded="xl" p={6}>Activity</Box>
</Grid>
<Grid xs={12} md={4}>
<Box variant="outlined" rounded="xl" p={6}>Recent</Box>
</Grid>
</Grid>
</Stack>
</Container>Container handles the page chrome (centered, padded, capped width). Stack/Grid handle the actual layout of children. Three components, three concerns, zero overlap.
A common pattern for "full-bleed background, capped content":
<Container size="fluid" px={false} sx="bg-primary-50 py-16">
<Container size="lg">
<Typography variant="h2" align="center">Section title</Typography>
<Typography variant="body1" align="center" color="muted">
Background spans the viewport. Content stays capped at 1024px.
</Typography>
</Container>
</Container>The outer Container provides the colored band edge-to-edge; the inner Container caps the text width for readability.
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'fluid' | 'xl' | Max-width. 'fluid' = no cap. |
px | boolean | true | Responsive horizontal padding ramp (px-4 sm:px-6 lg:px-8). |
centerContent | boolean | false | Stack children as a centered flex column. |
as | ElementType | 'div' | Override the HTML tag. |
asChild | boolean | false | Render via Radix Slot onto the single child. Wins over as. |
sx | string | — | Utility-class override, merged via tailwind-merge. |
| ...rest | HTMLAttributes<HTMLDivElement> | — | Native attributes pass through. |
import { containerVariants } from '@dashforge/tw';
// containerVariants({ size: 'lg', px: true }) → className stringsx="py-12" when you need vertical breathing room — the choice usually varies per page/section, so a default would be wrong more often than right.fluid keeps mx-auto + w-full — that's a no-op for fluid (the width is already 100%), but consistent with the baseline so swapping size doesn't suddenly drop the centering.'xl' — chosen for "full app with sidebar". For docs-only / marketing-only apps, set size="lg" at the page root explicitly.sx="w-64" or similar.