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.
Configure <Container> defaults application-wide through the theme.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
Container: {
defaults: { size: 'lg', px: 4, centerContent: false },
},
},
});Configurable axes (ContainerVariantProps):
| Axis | Type | Notes |
|---|---|---|
size | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'fluid' | Max-width tier. 'fluid' = no cap. |
px | boolean | Responsive horizontal padding ramp (px-4 sm:px-6 lg:px-8). |
centerContent | boolean | Center children with mx-auto + flex-column. |
Non-visual axes (as, asChild, event handlers, ARIA) are not theme-configurable — per-instance choice.
Precedence chain (lowest → highest):
defaultVariants from the internal containerVariants recipe (size: 'xl', px: true).theme.components.Container.defaults (application-wide).<Container size="lg" />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.Container.defaults autocompletes to the three axes above.
Reactivity: useComponentDefaults('Container') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | — | Override the rendered HTML tag. Defaults to 'div'. Use as="main" for the primary content region of a page, as="section" for a named section, as="article" for a self-contained article shell.Ignored when asChild is true. |
asChild | boolean | — | Render via Radix Slot — the Container styles paint onto the single React child instead of wrapping it. Useful for letting a router <main> element receive the Container chrome without adding an intermediate <div> in the tree.Mutually exclusive with as (when both are passed, asChild wins). |
centerContent | boolean | false | Center children as a flex column (mx-auto + flex flex-col items-center). |
px | boolean | true | Responsive horizontal padding ramp (px-4 sm:px-6 lg:px-8). Set to false to opt out (rare — usually you want the ramp). |
size | 'md' | 'sm' | 'lg' | 'xl' | '2xl' | 'fluid' | 'xl' | Max-width tier. 'fluid' disables the cap and keeps w-full. |
sx | string | — | Utility classes appended to the variant chain. Resolved via tailwind-merge so the consumer's classes always win over the variant defaults — useful for one-off vertical padding (sx="py-8"), custom max-width (sx="max-w-3xl"), etc. |
...rest | Omit<HTMLAttributes<HTMLDivElement>, 'className'> | — | Additional native attributes forwarded via Omit<HTMLAttributes<HTMLDivElement>, 'className'>. |
<Container> has no slots. It renders a single DOM element (defaulting to <div>, overridable via as or asChild) — there is no anatomy to target separately.
The slotProps API pattern only exists on compound components with multiple visually distinct regions. Examples: <Menu>, <DataGrid>, <Card>.
For <Container>, use sx for any style override.
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.