Docs / Density System
Density System
Three density presets -- compact, comfortable, and spacious -- that adapt every component to your workspace needs. One prop changes everything: row heights, font sizes, padding, icon sizes, and spacing.
Three modes, one API
Every Substrate component reads the current density from context and adjusts its sizing accordingly. You control density at any level of your component tree -- globally, per-page, or per-panel.
Compact
The compact-first default. Ideal for data-heavy interfaces like spreadsheets, admin dashboards, log viewers, and IDE-style tools where screen real estate is precious.
Best for
- ✓Data grids and tables with many rows
- ✓File browsers and tree views
- ✓IDE panels and terminals
- ✓Admin dashboards with dense data
Comfortable
A balanced option for professional applications that need more breathing room while keeping information accessible.
Best for
- ✓SaaS dashboards and control panels
- ✓Project management tools
- ✓CRM and business applications
- ✓General-purpose application UIs
Spacious
Maximum readability and touch-friendly sizing. Best for consumer-facing features, onboarding flows, settings pages, and any context where ease of use matters more than density.
Best for
- ✓Settings and configuration pages
- ✓Onboarding and setup flows
- ✓Consumer-facing dashboards
- ✓Mobile and touch interfaces
DensityProvider
Wrap your application or any subtree with DensityProvider to set the density for all child components:
"use client";
import { DensityProvider, useDensityPreference } from "@/lib/density";
// Compact-first globally, then restore the saved preference.
export function DensityRoot({ children }: { children: React.ReactNode }) {
const { density } = useDensityPreference();
return <DensityProvider mode={density}>{children}</DensityProvider>;
}
// A subtree can opt into a fixed mode.
export function CompactSidebar({ children }: { children: React.ReactNode }) {
return <DensityProvider mode="compact">{children}</DensityProvider>;
}| Prop | Type | Default | Description |
|---|---|---|---|
| mode | "compact" | "comfortable" | "spacious" | Required | Density mode supplied to the subtree |
| children | React.ReactNode | -- | Child components |
useDensityPreference() hook
Start compact, restore the saved choice, and persist updates with useDensityPreference(). The shared storage key is substrateui:density.
"use client";
import { Button } from "@/components/ui/button";
import { useDensityPreference } from "@/lib/density";
export function DensityToggle() {
const { density, setDensity } = useDensityPreference();
return (
<div className="flex gap-1">
{(["compact", "comfortable", "spacious"] as const).map((mode) => (
<Button
key={mode}
variant={density === mode ? "secondary" : "ghost"}
onClick={() => setDensity(mode)}
>
{mode}
</Button>
))}
</div>
);
}The hook returns:
- --
density-- The current density mode string - --
setDensity-- Function to update the density mode
How components respond
Every Substrate component internally calls useDensity() and maps its mode to a design-system size variant. The hook also exposes the resolved config.
import { Button } from "@/components/ui/button";
import { densityToSize, useDensity } from "@/lib/density";
function InspectorAction() {
const { mode } = useDensity();
return <Button size={densityToSize[mode]}>Apply</Button>;
}Density values reference
Complete mapping of density values across all three modes:
| Property | Compact | Comfortable | Spacious |
|---|---|---|---|
| Row height | 28px | 36px | 44px |
| Font size | 12px | 13px | 14px |
| Line height | 1.3 | 1.4 | 1.5 |
| Icon size | 14px | 16px | 18px |
| Horizontal padding | 8px | 12px | 16px |
| Vertical padding | 4px | 8px | 12px |
| Gap | 4px | 8px | 12px |
| Border radius | 2px | 4px | 6px |
| Input height | 28px | 36px | 44px |
| Button height | 28px | 36px | 44px |
| Avatar size | 20px | 24px | 32px |
| Checkbox size | 14px | 16px | 18px |
When to use each mode
Density is not a one-size-fits-all setting. Professional applications often use different densities in different sections:
Mixed density layout
A typical pro workspace might use compact for the sidebar navigation and tree views, comfortable for the main content area, and spacious for settings pages and onboarding flows.
User preference
Let power users choose their preferred density with useDensityPreference. It persists the choice across sessions under substrateui:density.
Responsive density
Consider switching to spacious on mobile devices where touch targets need to be larger, and compact on large monitors where users want maximum information density.