Split
Sidebar + main layout that stacks on small screens
Sidebar
Main content
import { Split } from '@/components/split';
export default function SplitPreview() {
return (
<Split>
<Split.Pane width="12rem">
<div className="rounded-lg border border-border bg-background-secondary p-4 text-foreground-secondary text-sm">
Sidebar
</div>
</Split.Pane>
<Split.Main>
<div className="rounded-lg border border-border bg-background-secondary p-4 text-foreground-secondary text-sm">
Main content
</div>
</Split.Main>
</Split>
);
} Source Code
import type { VariantProps } from 'cva';
import { cn, cva } from '@/lib/utils/classnames';
const splitStyle = cva({
base: 'flex w-full',
variants: {
direction: {
row: 'flex-col md:flex-row',
col: 'flex-col',
},
gap: {
none: 'gap-0',
sm: 'gap-4',
md: 'gap-6',
lg: 'gap-10',
},
},
defaultVariants: {
direction: 'row',
gap: 'md',
},
});
interface SplitProps
extends React.ComponentPropsWithRef<'div'>,
VariantProps<typeof splitStyle> {}
const Split = ({ ref, className, direction, gap, ...rest }: SplitProps) => {
return (
<div
ref={ref}
className={cn(splitStyle({ direction, gap }), className)}
{...rest}
/>
);
};
interface SplitPaneProps extends React.ComponentPropsWithRef<'div'> {
/** Fixed width of this pane (px or rem). Default 16rem. Pass `null` to flex. */
width?: string | number | null;
}
const SplitPane = ({
ref,
className,
width = '16rem',
style,
...rest
}: SplitPaneProps) => {
const isFixed = width !== null;
const widthValue =
typeof width === 'number' ? `${width}px` : (width ?? undefined);
return (
<div
ref={ref}
className={cn(
isFixed ? 'shrink-0' : 'min-w-0 flex-1',
'w-full md:w-(--split-pane-width)',
className
)}
style={
isFixed
? { ['--split-pane-width' as string]: widthValue, ...style }
: style
}
{...rest}
/>
);
};
const SplitMain = ({
ref,
className,
...rest
}: React.ComponentPropsWithRef<'div'>) => {
return (
<div ref={ref} className={cn('min-w-0 flex-1', className)} {...rest} />
);
};
const CompoundSplit = Object.assign(Split, {
Pane: SplitPane,
Main: SplitMain,
});
export type { SplitPaneProps, SplitProps };
export { CompoundSplit as Split, splitStyle }; Server component. No
'use client'— split-pane layout primitive; no hooks or own handlers.
Split provides a fixed-width pane next to a flexible main region. On small
screens it collapses to a vertical stack.
Anatomy
<Split>
<Split.Pane width="16rem">{sidebar}</Split.Pane>
<Split.Main>{main}</Split.Main>
</Split>
API Reference
Split
Extends the div element.
| Prop | Default | Type |
|---|---|---|
direction | "row" | "row""col" |
gap | "md" | "none""sm""md""lg" |
Split.Pane
Extends the div element.
| Prop | Default | Type |
|---|---|---|
width | "16rem" | stringnumber |
Split.Main
Extends the div element. Fills remaining space.
Examples
Default
Sidebar
Main content
import { Split } from '@/components/split';
export default function SplitPreview() {
return (
<Split>
<Split.Pane width="12rem">
<div className="rounded-lg border border-border bg-background-secondary p-4 text-foreground-secondary text-sm">
Sidebar
</div>
</Split.Pane>
<Split.Main>
<div className="rounded-lg border border-border bg-background-secondary p-4 text-foreground-secondary text-sm">
Main content
</div>
</Split.Main>
</Split>
);
} Previous
Spinner
Next
Stat