🔧 ProgrammierungWebKit Features for Safari 27.0(17.09.2026 um 13:30 Uhr)
🔧 ProgrammierungWebKit Features for Safari 27.0(17.09.2026 um 13:30 Uhr)
🔧 Programmierung 🕛 vor 1 Jahr 11 Min Lesezeit
0

How to build a dynamic website with Next.js 15 App Router, React 19, Storyblok, and Bun (+ Typescript)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

In this article, I would like to share with you how to create a dynamic web app with Next.js 15's App Router, React 19, and the Storyblok CMS, all powered by Bun for lightning-fast performance. This comprehensive guide walks you through setup, integration, and rendering dynamic content—perfect for modern developers.






Key takeaways from this guide




  • Learn how to set up a new Next.js app using Bun as the runtime, bundler, and package manager.

  • Install and configure the latest Storyblok React SDK for seamless CMS integration.

  • Understand how to manage your Storyblok API token securely using environment variables.

  • Discover how to fetch data from Storyblok and render it dynamically with Next.js 15's App Router.

  • Enable the Storyblok Visual Editor for an enhanced content editing experience.


  • Bonus: Explore how to use TypeScript definitions for strongly typed Storyblok components.






The tools



In this article, we’re combining some of the most cutting-edge tools in web development to create a powerful, efficient, and scalable web project:




  • Bun: more than just a runtime, Bun simplifies JavaScript development with lightning-fast performance, a built-in bundler, and an efficient package manager, an all-in-one tool. It's designed to save developers time while delivering exceptional speed.

  • Storyblok: as a headless CMS, Storyblok provides a flexible and user-friendly platform for managing content. With its Visual Editor and robust API, it empowers developers to integrate content dynamically while giving content creators an intuitive interface to work with.

  • Next.js App Router: the App Router in Next.js 15 unlocks the full potential of React Server Components, offering an innovative way to handle dynamic routing. It enables seamless integration of server-side and client-side rendering, improving performance and developer experience.






Set up your Next.js 15 project



Using Bun's command-line utility, create a Next.js 15 project:




CODE
bunx create-next-app@latest







The latest keyword will allow you to create a Next.js 15 project. If, for some reason, you prefer to use the Next.js 14 you can use 14 keyword instead of latest




Executing the create-next-app command, you need to answer some questions. For starting a basic project with TypeScript, here the options:



✔ What is your project named? my-nextjs15-storyblok

✔ Would you like to use TypeScript? Yes

✔ Would you like to use ESLint? No

✔ Would you like to use Tailwind CSS? No

✔ Would you like your code inside a src/ directory? Yes

✔ Would you like to use App Router? (recommended) Yes

✔ Would you like to use Turbopack for next dev? No

✔ Would you like to customize the import alias (@/* by default)? No



Once the command is executed, you can jump into the new directory my-nextjs15-storyblok where you can find your Next.js project with all the dependencies already installed.




CODE
cd my-nextjs15-storyblok









Enabling the HTTPS



For checking and testing the installation and starting the HTTPS protocol, you can use the dev script using the experimental HTTPS option:




CODE
bun run dev --experimental-https






As a developer, you should start a local HTTPS server when working with the Storyblok Visual Editor because the editor requires a secure connection to function correctly. Here's why:




  • Browser security requirements: modern browsers enforce strict security protocols for features like iframe communication, which the Visual Editor relies on. HTTPS ensures that the content loaded in the iframe (your local app) is secure and trusted, enabling the Visual Editor to work seamlessly.

  • Matching production environments: most production environments use HTTPS for secure communication. Running your local development server over HTTPS mirrors this setup, helping you catch potential issues early, such as mixed content warnings or problems with third-party integrations.

  • Storyblok’s integration policies: Storyblok’s Visual Editor is designed to work with secure connections, ensuring that the embedded content and API requests are encrypted. Without HTTPS, the Visual Editor might not function as intended or could block certain features.






Install Storyblok React SDK v4



To integrate Storyblok, install its Storyblok React SDK:




CODE
bun add @storyblok/react@4






This SDK (version 4) is fully compatible with Next.js 15 and supports the App Router's features.






Configure Storyblok



Sign in to Storyblok, create a space, and copy the API token (the Preview access token) from the "Settings -> Access Tokens" section.



If you are not familiar with creating space with Storyblok, here are some links:




  • Creating a space in Storyblok (yes, you can use the Community Plan for free):



We can set up the environment variables once you have your preview access token.

In your project root, create a .env.local file, and add:




CODE
NEXT_PUBLIC_STORYBLOK_TOKEN=your_token_here






So now you have your Storyblok SDK installed and your access token set up. Now, we can initialize the Storyblok connection in our Next.js project,






Initialize Storyblok in our project



Initialize Storyblok and register components in a src/lib/storyblok.ts file:




CODE
import DefaultPage from '@/components/DefaultPage';
import Teaser from '@/components/Teaser';
import HeroSection from '@/components/HeroSection';
import FallbackComponent from '@/components/FallbackComponent';
import { apiPlugin, storyblokInit } from '@storyblok/react/rsc';

export const getStoryblokApi = storyblokInit({
accessToken: process.env.NEXT_PUBLIC_STORYBLOK_TOKEN,
use: [apiPlugin],
components: {
teaser: Teaser,
"default-page": DefaultPage,
"hero-section": HeroSection,
},

enableFallbackComponent: true,
customFallbackComponent: FallbackComponent,
});






This code snippet demonstrates how to initialize Storyblok in a React application, set up components for dynamic rendering, enable API integration, and configure a fallback component. Here's a breakdown:






Imports






CODE
import DefaultPage from '@/components/DefaultPage';
import Teaser from '@/components/Teaser';
import HeroSection from '@/components/HeroSection';
import FallbackComponent from '@/components/FallbackComponent';
import { apiPlugin, storyblokInit } from '@storyblok/react/rsc';







  • Component Imports: these imports include your custom React components (DefaultPage, Teaser, HeroSection, and FallbackComponent), which will be rendered dynamically based on Storyblok content.

  • Storyblok features:



    • storyblokInit: Initializes Storyblok in the app.


    • apiPlugin: A plugin that allows API interaction with Storyblok.














Storyblok Initialization






CODE
export const getStoryblokApi = storyblokInit({
accessToken: process.env.NEXT_PUBLIC_STORYBLOK_TOKEN,
use: [apiPlugin],
components: {
teaser: Teaser,
"default-page": DefaultPage,
"hero-section": HeroSection,
},

enableFallbackComponent: true,
customFallbackComponent: FallbackComponent,
});








  • accessToken: the accessToken is retrieved from an environment variable (NEXT_PUBLIC_STORYBLOK_TOKEN), ensuring secure access to your Storyblok space.


  • use: the apiPlugin is registered to enable fetching content from Storyblok's API.


  • components: this object maps Storyblok content types (defined in your space, such as teaser, default-page, and hero-section) to their corresponding React components (Teaser, DefaultPage, and HeroSection). This ensures that the correct component is rendered based on the content type name or the component name.


  • enableFallbackComponent: when set to true, this enables using a fallback component if a component is not mapped or recognized.


  • customFallbackComponent: specifies a custom component (FallbackComponent) to display as the fallback. This is helpful for gracefully handling unmapped or unexpected content, such as showing an error message or placeholder content.









Exported Function






CODE
export const getStoryblokApi = storyblokInit({...});






This function initializes Storyblok and provides access to its API, allowing your application to interact with Storyblok's content delivery system dynamically.






Load Storyblok provider



In src/components/StoryblokProvider.tsx file you can initialize the Storyblok client:




CODE
'use client';

import { getStoryblokApi } from '@/lib/storyblok';

export default function StoryblokProvider({ children }) {
getStoryblokApi(); // Re-initialize on the client
return children;
}






Then, in the src/app/layout.tsx file, you can load the StoryblokProvider:




CODE
import type { Metadata } from "next";
import StoryblokProvider from '@/components/StoryblokProvider';


export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<StoryblokProvider>
<html lang="en">
<body>
{children}
</body>
</html>
</StoryblokProvider>
);
}










Set Up the App Router with a catch-all route



Next.js 15’s App Router uses a file-based routing system powered by the app directory. Let’s set up dynamic routes for Storyblok content.

We are going to create a "catch-all" route for creating the page.tsx file into the src/app/[slug]/ directory.



The src/app/[slug]/page.tsx file can be:




CODE
import styles from "../page.module.css";
import { getStoryblokApi } from '@/lib/storyblok';
import { StoryblokStory } from '@storyblok/react/rsc';

export async function fetchData(slug: string) {
const storyblokApi = getStoryblokApi();
return storyblokApi.get('cdn/stories/' + slug, { version: 'draft' });
}

export default async function StoryPage({params}
: {
params: Promise<{ slug: string }>
}) {
const slug = (await params).slug
console.log(slug)
const { data } = await fetchData(slug);

return (
<div>
<StoryblokStory story={data.story} />
</div>
);
}










Creating frontend components






The Content-Type component



First, you can create the frontend component to render the Storyblok content type. For example, I have the default-page Storyblok content type mapped into the DefaultPage React component (via the storyblok.ts file):



The src/components/DefaultPage.tsx file:




CODE
import { storyblokEditable, StoryblokServerComponent } from '@storyblok/react/rsc';

export default function DefaultPage({ blok }) {
return <main {...storyblokEditable(blok)}>
{blok.body.map(nestedBlok => (
<StoryblokServerComponent blok={nestedBlok} key={nestedBlok._uid} />
))}
</main>
}









The fallback component



Storyblok is very flexible. The Storyblok SDK via the StoryblokServerComponent and the StoryblokStory React components load dynamically React components based on the structure and the nested components defined in Storyblok. If a React component is not yet implemented on the frontend side, a fallback component can be loaded as a placeholder. The configuration of the fallback behavior is in our src/lib/storyblok.ts file in the storyblokInit function via:




CODE
enableFallbackComponent: true,
customFallbackComponent: FallbackComponent,






So, the fallback component is in src/components/FallbackComponent.tsx:




CODE
import { storyblokEditable } from '@storyblok/react/rsc';

const FallbackComponent = ({ blok }) => {
return (
<h2 data-cy="fallback" {...storyblokEditable(blok)}>
This component doesn't exists: {blok.component }
</h2>
);
};

export default FallbackComponent;










Your first Nestable component



For example, if you have a teaser Nestable component in Storyblok with a field named headline you can:




  • load the components in the storyblokInit function in the src/lib/storyblok.ts file.




CODE
  components: {
teaser: Teaser,
"default-page": DefaultPage,
"hero-section": HeroSection,
},







  • create your component in src/components/Teaser.tsx:




CODE
export default function Teaser({ blok }) {
return <>
{ blok.headline }
</>;
}









BONUS: using TypeScript and automatic generation of type definitions



As you can notice, probably your editor (I'm using Zed editor) will highlight some TypeScript warning for the type checking.

Now we are going to solve this issue, generating automatically all the types and the interfaces needed by your project, based on the component structure defined in Storyblok. Amazing!



I strongly recommend installing and using the Storyblok CLI. If you're exploring or actively using Storyblok, investing time to learn this powerful command-line tool is highly worthwhile. It's like a Swiss Army knife for Storyblok, offering a wide range of functionalities to manage content and components efficiently. One standout feature is its ability to generate type definitions.




More info about Storyblok CLI : to see your dynamic content rendered via Storyblok and the App Router.




If you want to use the Storyblok Visual Editor you can set the visual editor using the https://localhost:3000 as explained here:

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
2 Quellen
The amount of e-waste caused by AI is underestimated: we can’t only include the servers
1 Quelle
Crusoe raises $3.9B to build massive data centers and small modular “AI factories”
1 Quelle
GitHub Release: openai/codex vrust-v0.156.0-alpha.1 (18.09.2026)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How to build a dynamic website with Next.js 15 App Router, React 19, Storyblok, and Bun (+ Typescript)

Thematisch verwandte Begriffe: build, dynamic, website, with · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...