DEV Community

qudrat ullah
qudrat ullah

Posted on

How to Fix Chromium on Vercel: A Complete Guide to Solving the “libnspr4.so” Error

If you're deploying a Next.js app with Playwright and Chromium to Vercel, you've probably hit this error:

/tmp/chromium: error while loading shared libraries: libnspr4.so: cannot open shared object file: No such file or directory
Enter fullscreen mode Exit fullscreen mode

It works perfectly on your local machine but fails on Vercel. This is one of the most frustrating deployment issues developers face when working with headless browsers in serverless environments.

After analyzing 50+ GitHub issues and testing multiple solutions, I discovered the root cause and the complete fix. Here's everything you need to know.

Why It Works Locally But Fails on Vercel

The fundamental difference between your local environment and Vercel's serverless environment causes this issue.

On your local machine:

  • System libraries like libnspr4.so and libnss3.so are pre-installed
  • You have full file system access
  • Complete Node.js environment with all dependencies

On Vercel serverless:

  • Minimal Linux container with no system libraries
  • Read-only file system except for the /tmp directory
  • Environment variables must exist before modules load

The critical issue: The sparticuz/chromium package checks for AWS_LAMBDA_JS_RUNTIME when the module imports, but if you set it in your code, it happens after the module is already loaded. This timing mismatch breaks everything.

The Five Problems and Their Solutions

Problem 1: Wrong Package Choice

Many developers try to use the full Playwright package, which doesn't work on Vercel.

Don't use:

"playwright": "^1.39.0"
Enter fullscreen mode Exit fullscreen mode

This package exceeds Vercel's 50MB limit and tries to download the browser at runtime, which fails in serverless environments.

Use instead:

{
  "dependencies": {
    "playwright-core": "1.39.0",
    "@sparticuz/chromium": "^131.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Why this works: playwright-core is only about 5MB, and the sparticuz chromium package is about 40MB. Together they stay under Vercel's 50MB limit, and the chromium package provides a pre-bundled Chromium optimized for serverless environments.

Problem 2: Environment Variable Timing

This is where most developers get stuck. The timing of when environment variables are set matters.

The problem:

When you import the package, it immediately checks for AWS_LAMBDA_JS_RUNTIME. But if you set it in your code like this:

import chromiumPack from "@sparticuz/chromium";
process.env.AWS_LAMBDA_JS_RUNTIME = "nodejs22.x";
Enter fullscreen mode Exit fullscreen mode

The module has already loaded and checked for the variable. It's too late.

The solution:

Set the environment variable in the Vercel Dashboard, not just in your code.

Here's how:

  1. Go to Vercel Dashboard
  2. Select your project
  3. Go to Settings → Environment Variables
  4. Add a new variable: AWS_LAMBDA_JS_RUNTIME with value nodejs22.x
  5. Apply to Production, Preview, and Development environments
  6. Redeploy your application

Why the Dashboard matters: Environment variables set in the Vercel Dashboard are available before any modules load, which is exactly when the chromium package needs them.

Problem 3: Libraries Extracted But Not Found (The Critical Fix)

This was the breakthrough that solved the issue. Even when libraries are extracted correctly, Chromium can't find them.

The problem:

  • Libraries are extracted to /tmp/libnspr4.so and /tmp/libnss3.so
  • Chromium executable is in /tmp/chromium
  • But Chromium can't find the libraries because the system doesn't know where to look

The solution:

Set LD_LIBRARY_PATH to the executable directory before launching the browser.

Here's the code:

const executablePath = await chromiumPack.executablePath();
const execDir = path.dirname(executablePath);

// CRITICAL: Set LD_LIBRARY_PATH so Chromium can find libraries
process.env.LD_LIBRARY_PATH = execDir;
Enter fullscreen mode Exit fullscreen mode

Why this works: LD_LIBRARY_PATH tells the Linux system loader where to find shared libraries. By setting it to the same directory where the Chromium executable and libraries are located, Chromium can successfully load the required libraries.

This was the missing piece that most solutions don't mention. Without this, even with the correct environment variable, Chromium will fail to find the libraries.

Problem 4: Old Package Version

Using an outdated version of the chromium package can cause compatibility issues with Node.js 22.x.

Don't use:

"@sparticuz/chromium": "119.0.2"
Enter fullscreen mode Exit fullscreen mode

Use:

"@sparticuz/chromium": "^131.0.0"
Enter fullscreen mode Exit fullscreen mode

Version 131.0.0 has better support for Node.js 22.x and includes fixes for common serverless deployment issues.

Problem 5: Browser Freezing

Some developers report that the browser freezes after creating a new page, even when everything else is configured correctly.

The problem: Browser freezes after "Creating new page" message appears.

The solution:

Disable graphics mode before launching the browser:

if (typeof chromiumPack.setGraphicsMode === 'function') {
  chromiumPack.setGraphicsMode(false);
}
Enter fullscreen mode Exit fullscreen mode

Why: Serverless environments don't have GPU support. Disabling graphics mode prevents the browser from trying to use GPU features that aren't available, which causes freezing.

Complete Working Solution

Here's the complete implementation that works on Vercel:

1. Package.json Configuration

{
  "dependencies": {
    "@sparticuz/chromium": "^131.0.0",
    "playwright-core": "1.39.0"
  },
  "engines": {
    "node": "22.x"
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Next.js Configuration

In your next.config.ts file:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  serverExternalPackages: [
    'playwright-core',
    '@sparticuz/chromium',
  ],
};

export default nextConfig;
Enter fullscreen mode Exit fullscreen mode

This prevents Next.js from bundling these packages, which is essential for them to work correctly in the serverless environment.

3. Browser Launch Code

Create a file lib/browser.ts with this code:

import { chromium, Browser } from "playwright-core";
import chromiumPack from "@sparticuz/chromium";
import * as path from "path";

let browserInstance: Browser | null = null;

export async function getBrowser(): Promise<Browser> {
  if (browserInstance) return browserInstance;

  const isVercel = !!(process.env.VERCEL || process.env.VERCEL_ENV);

  if (isVercel) {
    // Set runtime (fallback if not in Dashboard)
    if (!process.env.AWS_LAMBDA_JS_RUNTIME) {
      process.env.AWS_LAMBDA_JS_RUNTIME = "nodejs22.x";
    }

    // Set graphics mode to prevent freezing
    if (typeof chromiumPack.setGraphicsMode === 'function') {
      chromiumPack.setGraphicsMode(false);
    }

    // Get executable and set library path
    const executablePath = await chromiumPack.executablePath();
    const execDir = path.dirname(executablePath);

    // CRITICAL: Set LD_LIBRARY_PATH
    process.env.LD_LIBRARY_PATH = execDir;

    browserInstance = await chromium.launch({
      args: chromiumPack.args,
      executablePath,
      headless: true,
    });
  } else {
    // Local development
    browserInstance = await chromium.launch({
      args: ["--no-sandbox"],
      headless: true,
    });
  }

  return browserInstance;
}
Enter fullscreen mode Exit fullscreen mode

4. API Route Configuration

In your API route file (e.g., app/api/screenshot/route.ts):

import { NextRequest, NextResponse } from "next/server";
import { getBrowser } from "@/lib/browser";

export const runtime = "nodejs";
export const maxDuration = 300;
export const dynamic = "force-dynamic";

export async function POST(request: NextRequest) {
  const browser = await getBrowser();
  // Your screenshot code here
}
Enter fullscreen mode Exit fullscreen mode

The runtime = "nodejs" ensures the function runs in the Node.js runtime, and maxDuration = 300 gives you 5 minutes for screenshot operations (requires Vercel Pro plan).

5. Vercel Dashboard Settings

Before deploying, configure these settings in your Vercel Dashboard:

Required settings:

  1. Environment Variable: Add AWS_LAMBDA_JS_RUNTIME with value nodejs22.x
  2. Disable Fluid Compute: Go to Settings → Functions → Fluid Compute → Turn OFF
  3. Pro Plan: Required for 300-second timeouts (Hobby plan only allows 10 seconds)

The Three Critical Fixes

You need all three of these fixes for Chromium to work on Vercel:

  1. Correct packages: Use playwright-core and sparticuz chromium version 131.0.0 or higher
  2. Environment variable: Set AWS_LAMBDA_JS_RUNTIME=nodejs22.x in Vercel Dashboard
  3. Library path: Set LD_LIBRARY_PATH to the executable directory in your code

Missing any one of these will cause the deployment to fail. They work together:

  • The environment variable tells the package which runtime to use
  • The library path tells Chromium where to find the extracted libraries
  • The correct packages ensure everything fits within Vercel's limits

Quick Troubleshooting Checklist

If you're still experiencing issues, check each of these:

  • [ ] AWS_LAMBDA_JS_RUNTIME=nodejs22.x is set in Vercel Dashboard (not just in code)
  • [ ] LD_LIBRARY_PATH is set in code before browser launch
  • [ ] Using playwright-core (not the full playwright package)
  • [ ] Using sparticuz chromium version 131.0.0 or higher
  • [ ] setGraphicsMode(false) is called before launching the browser
  • [ ] Fluid Compute is disabled in Vercel settings
  • [ ] You're on Vercel Pro plan for extended timeouts
  • [ ] serverExternalPackages is configured in next.config.ts

Why This Solution Works

This solution addresses all the fundamental differences between local and serverless environments:

  1. Package choice: playwright-core and the chromium package are designed for serverless and stay within size limits
  2. Environment variable: Setting it in the Dashboard ensures it's available when modules load
  3. Library path: LD_LIBRARY_PATH tells the system where to find shared libraries in the minimal serverless environment

The critical insight: Both the environment variable (for runtime detection) and the library path (for finding libraries) are required. Most solutions only mention one or the other, but you need both.

Conclusion

Deploying Chromium on Vercel requires three essential components:

  • The right packages (playwright-core and the chromium package)
  • Environment variable configured in Vercel Dashboard
  • Library path set in your code

Once you have all three configured correctly, your screenshot application will work reliably on Vercel's serverless infrastructure. The key is understanding that serverless environments are fundamentally different from local development, and each difference requires a specific solution.

This solution was discovered after analyzing 50+ GitHub issues and testing multiple approaches. The breakthrough was realizing that LD_LIBRARY_PATH must be set to the executable directory, which most documentation doesn't mention.

Resources


If you found this helpful, feel free to share your experience or ask questions in the comments below!

Top comments (0)