Guarantee Code Never Crosses the Boundary
server-only & client-only
Import `server-only` at the top of a module and the build will fail if that module is ever pulled into client code.
What you'll learn
- Use `import 'server-only'` in DB and secret modules
- Use `import 'client-only'` for browser-only modules
- Catch leaks at build time rather than runtime
server-only is a tiny package that throws if you ever import it from client code. The
purpose is build-time insurance — accidentally bundling a DB client or secret would be
disastrous, and a runtime check would catch it too late.
Protect a Server Module
// lib/db.ts
import 'server-only'
import { PrismaClient } from '@prisma/client'
export const db = new PrismaClient() If a Client Component imports @/lib/db, the Next build now fails with a clear error:
“This module cannot be imported from a Client Component module.”
Protect a Client Module
// lib/dom-utils.ts
import 'client-only'
export function getScrollY() {
return window.scrollY
} Importing client-only into a Server Component fails the build the same way. Use it on
modules that touch window, document, browser-only APIs, or third-party widgets.
When to Reach For Them
Add server-only to anything that handles secrets — DB clients, env var modules,
private SDKs. Add client-only to anything that requires a browser. The few extra
lines pay off the first time they catch a bug you would have shipped.