server-only & client-only

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.

3 min read Level 2/5 #nextjs#server-only#security
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.

The Edge Runtime →