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

28px rows12px font

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

36px rows13px font

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

44px rows14px font

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>;
}
PropTypeDefaultDescription
mode"compact" | "comfortable" | "spacious"RequiredDensity mode supplied to the subtree
childrenReact.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:

PropertyCompactComfortableSpacious
Row height28px36px44px
Font size12px13px14px
Line height1.31.41.5
Icon size14px16px18px
Horizontal padding8px12px16px
Vertical padding4px8px12px
Gap4px8px12px
Border radius2px4px6px
Input height28px36px44px
Button height28px36px44px
Avatar size20px24px32px
Checkbox size14px16px18px

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.