Third-party libraries in Server Components fail for one of two unrelated reasons, and this article covers the fix for each. Some npm packages error the moment you import them into a Server Component, even though the same package works fine in a Client Component.
The first cause is a UI package that uses state or browser features internally but never marks itself as client code. The second cause is a server-side package that depends on Node.js APIs, which Next.js should not try to send to the browser at all. Applying the wrong fix for either one does not work.
Why a UI package breaks in a Server Component
A Server Component runs on the server by default, so it cannot use React state, effects, or browser APIs. Next.js decides whether a component is allowed to do that by checking whether its file, or a file it imports, starts with the use client directive.
Many published UI libraries call hooks internally but ship without that directive, because the package predates the convention or the author only tested it inside a Client Component. Next.js cannot see inside the package's compiled code and infer that it needs the client runtime, so the build fails instead of silently working.
// app/gallery.tsx
import { Carousel } from 'acme-carousel'
export default function Gallery() {
return <Carousel images={['/one.jpg', '/two.jpg']} />
}This file has no directive, so Gallery stays a Server Component. Carousel calls a state hook inside the acme-carousel package, and that call fails because it runs in a context that never became a Client Component.
Fixing it with a local wrapper
The fix is a small file of your own, marked with the directive, that re-exports the package's component. Once that wrapper file is a Client Component, importing it from anywhere else works normally, including from a Server Component.
// app/ui/carousel.tsx
'use client'
import { Carousel } from 'acme-carousel'
export default CarouselThis wrapper runs on the client. It does nothing to the package itself, it only gives the package's component a home inside a module Next.js already treats as a client boundary.
Now the Server Component imports the wrapper instead of the package directly.
// app/gallery.tsx
import Carousel from './ui/carousel'
export default function Gallery() {
return <Carousel images={['/one.jpg', '/two.jpg']} />
}Gallery still runs on the server and still has no directive of its own. Carousel now resolves to the wrapper file, a Client Component, so the package's internal state hook runs where React allows it. The visible result is the same carousel UI, but the build succeeds instead of erroring.
If you use the same package inside a component that is already a Client Component, you do not need the wrapper at all. Importing the package directly works there, because the whole file is already past the client boundary. The wrapper only matters when a Server Component needs to render that package.
Wrapping with extra props or styling
A wrapper file is also useful when you want to fix default props or add a class name without changing every call site. The wrapper stays a thin pass-through, so it still behaves like the original component everywhere it is used.
// app/ui/carousel.tsx
'use client'
import { Carousel } from 'acme-carousel'
export default function AppCarousel(props: React.ComponentProps<typeof Carousel>) {
return <Carousel autoplay {...props} />
}This file also runs on the client. It sets a default for the autoplay prop while still accepting every other prop the original component accepts, so callers can override it the same way they would with the package directly. This pattern is worth reaching for anytime several pages need the same defaults applied to a third-party component, instead of repeating those props at every call site.
Advice if you are the package author
If you publish a component library, add the directive to entry points that use hooks or browser APIs. Consumers can then import your components straight into their Server Components without writing a wrapper of their own. Some bundlers strip directives during the build, so confirm your build output still includes the string before publishing.
The other problem: Node.js-only server packages
serverExternalPackages solves a different problem than the wrapper pattern above. Next.js bundles the dependencies used inside Server Components and route handlers by default, the same way it bundles the rest of an app. Some packages use native bindings or other Node.js-specific features that this bundling process cannot handle correctly.
Add the package name to this config key in next.config.ts to tell Next.js not to bundle it, and to load it natively from Node.js at runtime instead.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
serverExternalPackages: ['@acme/native-pdf'],
}
export default nextConfigserverExternalPackages is stable, not experimental, as of Next.js 15. It replaced the older experimental.serverComponentsExternalPackages key, so if an older guide uses that name, treat it as the same setting under its current name.
This config file runs only during the build. Nothing here ships to the browser, and the array only changes how Next.js bundles the listed packages for Server Components and route handlers.
Next.js already excludes some packages automatically
Next.js maintains a built-in list of popular server-side packages it already treats as external without any configuration, including sharp, prisma, better-sqlite3, puppeteer, and pino. You do not need to add these packages yourself.
| Situation | What to do |
|---|---|
| Package is already on the built-in external list | Nothing, it works without config |
| Package uses Node.js APIs and is not on the list | Add it to serverExternalPackages |
| Package uses browser APIs or hooks with no directive | Wrap it in your own client file |
Add a package to this config only after a build error points at that dependency during Server Component bundling. Adding packages speculatively opts them out of optimizations Next.js could otherwise apply, without any real benefit.
Do not mix up the two fixes
A serverExternalPackages entry does nothing for a package that fails because it calls a state hook without the directive, because that failure is a missing client boundary, not a bundling problem. A client wrapper does nothing for a package that fails because it calls a native Node.js binding, because wrapping it in a client file only makes Next.js try to send that native code to the browser, which fails differently again.
Check the error message before choosing a fix. A complaint about hooks or browser globals needs the wrapper pattern from earlier in this article. A complaint about native modules or other Node.js-specific behavior during the server bundle needs the config key instead.
For more on how the client boundary itself works, see The use client Directive Explained and Next.js Server vs Client Boundary Explained. For the broader set of patterns for mixing the two component types, see Composition Patterns for Server and Client Components.
Rune AI
Key Insights
- Many UI packages use useState, useEffect, or browser globals without shipping their own use client directive.
- Importing such a package directly into a Server Component fails because Next.js cannot tell it needs the client runtime.
- The fix is a small local file marked use client that re-exports the package's component, then importing that wrapper instead of the package.
- serverExternalPackages solves a different problem: it excludes Node.js-only server packages from Server Component bundling and requires them natively at runtime.
- Next.js already treats a built-in list of popular packages, like sharp, prisma, and better-sqlite3, as external automatically.
- Never use serverExternalPackages to fix a client-only package, and never use a use client wrapper to fix a Node.js-only package.
Frequently Asked Questions
Why does importing a UI library directly into a Server Component fail?
Does wrapping a package in a use client file change how the package behaves?
Is serverExternalPackages the fix for a client-only UI library?
Do I need to list every server dependency in serverExternalPackages?
Conclusion
Third-party packages break in Next.js for two unrelated reasons. A client-only UI package needs a small use client wrapper file so Next.js knows to bundle it for the browser, while a Node.js-only server package needs serverExternalPackages so Next.js stops trying to bundle it at all and requires it natively instead.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.