Skip to content
Drivn
6 min read

Next.js Separator with the App Router

Add a separator to a Next.js App Router app. Drivn ships a copy-paste, server-safe divider themed by one border token, with zero runtime dependencies.

A separator is the quiet piece of structure that groups a layout — a hairline between a card header and its body, a divider inside a dropdown menu, a thin rule between two columns of a toolbar. In a Next.js App Router project the useful thing to know up front is that Drivn's Separator is purely presentational: it holds no state and calls no browser API, so separator.tsx ships without a 'use client' directive and renders straight inside a Server Component.

After install the component sits in src/components/ui/separator.tsx as a single function — a <div> with role="separator" whose class is chosen from a two-key styles object by the orientation prop. styles.horizontal is w-full h-px bg-border, a one-pixel-tall line that fills its container's width; styles.vertical is h-full w-px bg-border, a one-pixel-wide line that fills its container's height. Both tint themselves with the --border theme token, so the rule matches your palette in dark and light mode with no override.

This guide installs Drivn in a Next.js 16 project, renders a horizontal divider inside a Server Component, stands a vertical separator up inside a flex row, and restyles the rule from the one styles object you own. Every snippet comes from the component's real API. For the full reference see the Separator docs; for the shadcn/ui comparison see Drivn vs shadcn/ui Separator.

Install in a Next.js 16 project

Drivn installs through a small CLI that writes component source directly into your repository — there is no runtime package to version-lock. From the root of your Next.js 16 project run npx drivn add separator. The Separator imports nothing past React and the local cn utility, so the CLI writes a single separator.tsx file and leaves the rest of your tree untouched. The CLI reference documents every flag, including targeting a custom directory or installing several components at once. After install you own the file, and future Drivn releases will not overwrite it — commit it for a clean baseline before wiring it into a layout. If the same screen groups content into panels, pair the divider with a Drivn Card from the same install.

1# from the root of your Next.js 16 project
2npx drivn add separator

Render a horizontal divider in a Server Component

Because the Separator holds no state and calls no browser API, separator.tsx ships without a 'use client' directive — you can render it straight inside a Server Component in a Next.js App Router page, no client boundary required. Import Separator and drop it between two blocks of content; with no props it defaults to orientation="horizontal", which applies w-full h-px bg-border and stretches a one-pixel rule across the full width of its parent. Because the width comes from w-full, the parent element decides how wide the line runs — put the divider inside a fixed-width card or a padded section and it spans exactly that box. The role="separator" on the underlying <div> means assistive technology announces it as a divider rather than skipping past it as empty markup, so the grouping you see is the grouping a screen reader hears.

1import { Separator } from '@/components/ui/separator'
2
3export function AccountHeader() {
4 return (
5 <div>
6 <h3 className="font-medium">Account</h3>
7 <Separator />
8 <p className="text-sm text-muted-foreground">
9 Manage your profile and settings.
10 </p>
11 </div>
12 )
13}

Stand a vertical separator inside a flex row

Pass orientation="vertical" to switch the rule from a horizontal line to a vertical one. The component reads that prop and swaps styles.horizontal for styles.verticalh-full w-px bg-border — a one-pixel-wide line that fills its parent's height instead of its width. The catch is the mirror of the horizontal case: a vertical separator needs its parent to give it a height to fill, so it belongs inside a flex row where the row's height defines the line. Set the container to flex items-stretch (or give the separator an explicit h-* through className) so the rule runs the full height between the items it divides. This is the shape for a toolbar of icon buttons, a set of inline stats, or a nav-style row where a thin vertical rule reads cleaner than extra gap.

1<div className="flex items-stretch h-8 gap-4">
2 <span>Drafts</span>
3 <Separator orientation="vertical" />
4 <span>Sent</span>
5 <Separator orientation="vertical" />
6 <span>Archive</span>
7</div>

How the orientation styles work

Every class the component renders lives in one small styles object at the top of the file, and the whole component is a single <div> that picks a key from it. styles.horizontal is w-full h-px bg-border and styles.vertical is h-full w-px bg-border; the function looks up styles[orientation], merges it with any className through cn(), and renders a <div> carrying role="separator". There is no state, no effect, and no icon — the orientation prop defaults to 'horizontal', so an unadorned <Separator /> is the horizontal line. Because both keys tint the rule with bg-border, the divider reads with your theme's border token and needs no dark-mode override. The verbatim source below is the entire component — a two-key style map and a small function.

1// verbatim from separator.tsx
2const styles = {
3 horizontal: 'w-full h-px bg-border',
4 vertical: 'h-full w-px bg-border',
5}
6
7export function Separator({
8 orientation = 'horizontal',
9 className,
10}: SeparatorProps) {
11 return (
12 <div
13 role="separator"
14 className={cn(styles[orientation], className)}
15 />
16 )
17}

Restyle and theme the separator

Because the rule is a plain <div> tinted by bg-border, restyling it is a one-line change or a per-instance className. Make it bolder by bumping h-px to h-0.5 in styles.horizontal; recolor it by swapping bg-border for bg-primary/30; or add breathing room where you use it with a margin utility like my-4. The className you pass merges after the base style through cn(), so a single call site can thicken, tint, or space one divider without touching the shared file — <Separator className="my-6 bg-primary/20" /> is a themed, spaced rule at one call site. Since the default reads the --border token, adjusting that value in theming re-themes every separator across the app for dark and light mode at once. For copy-paste layouts that use the divider in menus, cards, and toolbars, see the Separator examples.

1// thicker, tinted rule in separator.tsx
2horizontal: 'w-full h-0.5 bg-primary/30',
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

Yes. The Separator holds no state and calls no browser API — it is a styled <div> that maps an orientation prop to a Tailwind class. Its source ships without a 'use client' directive, so it renders straight inside a Server Component in the Next.js App Router, and it works the same in any client-rendered React app. Nothing about it forces a client boundary, which makes it free to drop into a server-rendered layout, card, or content page.

Pass orientation="vertical" to the Separator. The default horizontal applies w-full h-px bg-border; vertical swaps in h-full w-px bg-border through the two-key styles object. Then give the parent a height for the rule to fill — usually a flex items-stretch row — so the one-pixel line runs the full height between the items it divides. Without a height on the parent, a vertical separator has nothing to stretch against and will not appear.

A vertical separator is h-full w-px, so it fills its parent's height — and if the parent has no height, h-full resolves to zero and the line is invisible. Put the separator inside a flex row with items-stretch and a defined height, or give the separator itself an explicit height like h-6 through className. The same rule mirrors the horizontal case, where the line needs a parent width to span with w-full.

Yes. The separator is a <div> styled by two Tailwind strings in the styles object inside the file you own. Recolor it by editing bg-border, and thicken it by bumping h-px (horizontal) or w-px (vertical) to h-0.5 or w-0.5. Because the default reads the --border theme token, retheming that value updates every separator for dark and light mode at once. A one-off className merges after the base style, so a single call site can restyle one divider without touching the shared component.

Yes. The component has no dependency on Next.js or any router — it is a self-contained styled <div> with no state, no context, and no browser API, so it works anywhere React and Tailwind reach the DOM. In a Vite + React app there is no server boundary to think about; run npx drivn add separator and render it the same way, defaulting to a horizontal rule or passing orientation="vertical" inside a flex row.