Deploy Next.js with Storyblok on Netlify
Storyblok is the first headless CMS that works for developers & marketers alike.
You’ve chosen the best-in-class stack: Next.js for its flexible frontend and Storyblok for its Visual Editor that content teams love. But when it comes to deployment, a common question arises:
Do I have to stay inside Vercel’s ecosystem, or can I run this stack reliably on Netlify?
The answer: you can deploy on Netlify with confidence. This guide will not only walk you through the “how,” but also the “why.” By the end, you’ll have a production-grade, secure, and editor-friendly Next.js and Storyblok site running flawlessly on Netlify.
Configuring the Netlify deployment
Why isn’t every platform the same?
Next.js is maintained by Vercel, and its default build output is tailored to Vercel’s infrastructure. This means its build output–the serverless functions, edge middleware, and static assets–is designed to map perfectly and natively onto Vercel's infrastructure. This is a fantastic, streamlined experience.
However, relying on a single vendor for both your framework and your infrastructure can lead to lock-in. What if your organization prefers a different provider for security, multi-cloud strategy, or other features? This is where the importance of a decoupled architecture truly shines. Platforms like Netlify offer their own powerful primitives, and we need a way to translate Next.js's output to fit them.
The magic of Adapters: your Universal translator
Think of it like a travel plug adapter. Your application is a device with a specific plug (the Next.js build output), and the hosting provider is the wall socket. A deployment adapter's job is to make your app "fit" the hosting platform's infrastructure so everything just works.
For Next.js on Netlify, this is handled by the @netlify/plugin-nextjs adapter. It's an intelligent layer that Netlify automatically applies during the build process when it detects a Next.js project.
Use your preferred package manager and install it:
npm install @netlify/plugin-nextjs --save-dev
Configuring Netlify with netlify.toml
Netlify uses a netlify.toml
file at the root of your project to define how the site should be built and deployed. Think of it as the project manifest for Netlify, similar to how vercel.json
configures a deployment on Vercel.
At a minimum, add this file at the root of your repo:
[build]
command = "npm run build"
[[plugins]]
package = "@netlify/plugin-nextjs"
Deep dive: how the @netlify/plugin-nextjs
 adapter works
So what does this adapter actually do under the hood? It's a sophisticated mapping exercise:
- API Routes & SSR Pages to Netlify Functions:Â Any server-rendered page or API route in your Next.js app is bundled into a serverless Netlify Function. This allows for dynamic, on-demand execution without you needing to manage servers.
- Middleware to Netlify Edge Functions:Â Next.js Middleware is designed for speed, running before a request is fully processed. The adapter maps this functionality directly to Netlify Edge Functions, which run on Netlify's global edge network for minimal latency.
next/image
 to Netlify's Image CDN: The adapter intelligently rewrites calls to theÂnext/image
 component to use Netlify's powerful Image CDN. This handles on-the-fly optimization, resizing, and format conversion without requiring any extra configuration from you.
A nod to the ecosystem: what is OpenNext?
It's worth mentioning OpenNext, an open-source initiative focused on creating a universal adapter for deploying Next.js to various platforms, primarily AWS. This community-driven project highlights the collective desire for a decoupled web where developers have the freedom to choose their tools and infrastructure. The official Netlify adapter can be seen as a polished, officially supported solution that delivers this same freedom within the Netlify ecosystem.
Building a "Netlify-Ready" Next.js app
Now for the practical implementation of the Next.js codebase. We'll focus on the key areas that ensure a smooth and secure deployment.
Complete the Storyblok Next.js Guide. Work through the first few parts to connect your app, fetch data, and render basic components. Once you can pull content from Storyblok, return to this guide.
The golden rule of security: managing environment variables
This is arguably the most critical step for a secure, production-ready deployment on Netlify. Mismanaging API keys will, and should, fail your build.
Storyblok uses API tokens for both preview and published content. These must be handled correctly, and for this tutorial I suggest to handle your variables this way:
- STORYBLOK_PREVIEW_TOKEN
- Grants access to both draft (unpublished) and published content
- Should be used server-side only (e.g., in API routes, server components, or ‎
getStaticProps
) - Use this token for preview environments
- Never expose it to the browser, set is as “contains secret values” on Netlify
- STORYBLOK_PUBLIC_TOKEN
- Grants access only to published content
- Use this token for production environments where you don’t want to expose draft content
- Also server-side only, and should be marked as secret
- NEXT_PUBLIC_STORYBLOK_PREVIEW_TOKEN
- Grants access to draft and published content, but is intended for client-side use (e.g., live editing in the Storyblok Visual Editor) and only by the editors
- Because it’s prefixed with ‎
NEXT_PUBLIC_
, Next.js exposes it to the browser - Do not mark this as secret on Netlify
- Only use this in preview environments or when live editing is required
Netlify enforces this separation: if you accidentally expose a secret token, your build will fail. This is by design and helps protect your data.
If you follow our guide, you will create a file where you initialize the Storyblok client server-side. For the purpose of this guide, here you will use the secret preview token:
/**
* Initializes the Storyblok client and registers all components.
*/
export const storyblokApi = storyblokInit({
accessToken: process.env.STORYBLOK_PREVIEW_TOKEN,
use: [apiPlugin],
components,
apiOptions,
});
Then, you will create a StoryblokProvider
component, and that is the client component that you will use in the Visual Editor.
"use client";
import { storyblokInit, apiPlugin } from "@storyblok/react/rsc";
import { ReactNode } from "react";
import Teaser from "./Teaser";
const components = {
teaser: Teaser,
// Add other Storyblok components here
};
/**
* Initializes the Storyblok client for the Visual Editor.
* This needs to be a client component.
*/
storyblokInit({
accessToken: process.env.NEXT_PUBLIC_STORYBLOK_PREVIEW_TOKEN,
use: [apiPlugin],
components,
});
export default function StoryblokProvider({ children }: { children: ReactNode }) {
return children;
}
Notice how we load the exposed NEXT_PUBLIC_STORYBLOK_PREVIEW_TOKEN
that is included in the build. This is a client component, so this code, if bundled, will be exposed to the browser.
This component must be loaded only in draft mode, which we enable in our preview environments and only for our editors.
We covered all the necessary steps for enabling/disabling Draft Mode and conditionally loading the Storyblok Bridge in this tutorial: Enable Draft Mode in Next.js for Storyblok Visual Editor
Finally, to conclude this secure setup, we wrap our layout with the client component StoryblokProvider
when we are in draft mode.
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const isStoryblokPreview = (await draftMode()).isEnabled;
return (
<html lang="en">
<body>
{isStoryblokPreview ? (
<>
<DraftToolbar />
<StoryblokProvider>{children}</StoryblokProvider>
</>
) : (
children
)}
</body>
</html>
);
Our preview token (or public token) will never be included in the browser bundle, unless we explicitly enable the draft mode in a secure way.
The final piece: webhooks for a first-class content editor experience
In Storyblok, a webhook is the critical link that tells Netlify to rebuild your site whenever a content editor publishes a change. This creates a seamless workflow, removing the developer from the publishing process.
- Create a Build Hook in Netlify
- Navigate to your site's dashboard on Netlify
- Go to Project configuration > Build & deploy > Continuous deployment > Build hooks
- Click Add build hook. Give it a descriptive name (e.g., "Storyblok Publish") and select the branch you want to build (e.g., main).
- Save the hook, and Netlify will generate a unique URL for you to copy.
- Add the Webhook in Storyblok
- Go to your Storyblok space and click on Settings > Webhooks
- Click New Webhook
- Paste the Netlify build hook URL into the "Endpoint URL" field
- For the trigger, select Story > Story published
- Save your webhook
Now, when editors click Publish in Storyblok, Netlify rebuilds and redeploys automatically.
Bonus tip: incremental builds
For large-scale sites with thousands of pages, rebuilding the entire site for a small text change can be inefficient. The next evolution in this workflow is On-Demand Incremental Static Regeneration (ISR).
Netlify's adapter fully supports this feature. Instead of a full rebuild, you can configure a webhook to call a specific API route in your Next.js app that uses revalidateTag
 or revalidatePath
. This tells Next.js to regenerate only the specific pages affected by the content change, resulting in near-instant updates.
Follow the official Netlify documentation to know more.
True flexibility, no compromises
We’ve built a secure, scalable, and editor-friendly Next.js + Storyblok site on Netlify. Along the way, you learned:
- Why adapters matter and how Netlify’s Next.js plugin works
- How to manage environment variables securely
- How to set up Storyblok preview mode without exposing secrets
- How to connect Storyblok and Netlify via webhooks for a smooth publishing workflow
The big takeaway: you’re not locked in. With the right architecture, you can run a modern Next.js and Storyblok stack on any platform. Netlify’s adapter makes that experience first-class.