How to optimize your Storyblok app
Dušan Perković

Dušan Perković @noblica

About: Senior Software Engineer with a focus on all things Frontend. Having some adventures in Go. Interested in web3.

Location:
Belgrade, Serbia
Joined:
Aug 8, 2023

How to optimize your Storyblok app

Publish Date: Aug 15 '23
3 2

If you are using @storyblokcom with NextJS, and following the official guides from Storyblok, you know that the usual way to start the project is:

  1. Create a NextJS project using create-next-app
  2. Install the necessary dependencies to integrate with Storyblok.
  3. Create your Storyblok components.
  4. Initialize the NextJS - Storyblok connection by adding the following code to your _app.jsx.
...

import { storyblokInit, apiPlugin } from "@storyblok/react";
import Feature from "../components/Feature";
import Grid from "../components/Grid";
import Page from "../components/Page";
import Teaser from "../components/Teaser";

const components = {
  feature: Feature,
  grid: Grid,
  teaser: Teaser,
  page: Page,
};

storyblokInit({
  accessToken: "your-preview-token",
  use: [apiPlugin],
  components,
});

...
Enter fullscreen mode Exit fullscreen mode

The problem:

You are loading all of your components on each page load!

This is because the contents of _app.jsx get executed on every page. So all the JavaScript that is imported in the _app.jsx file, gets bundled and sent to the client on every page load.

Since the official guide recommends importing all of your components in the _app.jsx file, this means that all your component code get loaded on every page. Even if that page is completely static and doesn't use any StoryBlok components at all!

I ended up creating a Github issue, and implementing this feature via a PR, on the official @storyblok/react Github repo. I was a bit hesitent about contributing, but the good folks at the @storyblok/react repo were very kind and accepting of it, so a big thanks to them!

How to load only the stuff you need

Instead of loading all your components in the _app.jsx file, just initialize the connection, and remove any component imports you have, so it looks more like this:

...

import { storyblokInit, apiPlugin } from "@storyblok/react";

storyblokInit({
  accessToken: "your-preview-token",
  use: [apiPlugin],
});

...
Enter fullscreen mode Exit fullscreen mode

Then, in your page components, use the new setComponents method, to set only the components you need on that page. So it would look something like this:

...

import { getStoryblokApi, StoryblokComponent, setComponents } from "@storyblok/react"
import Feature from "../components/Feature";
import Grid from "../components/Grid";
import Page from "../components/Page";
import Teaser from "../components/Teaser";

export default function Home(props) {
    const story = props.story
    setComponents({
      feature: Feature,
      grid: Grid,
      teaser: Teaser,
      page: Page,
    })

    return (
      <div className={styles.container}>
        <Head>
          <title>Create Next App</title>
          <link rel="icon" href="/favicon.ico" />
        </Head>

        <header>
          <h1>
            { story ? story.name : 'My Site' }
          </h1>
        </header>

         <StoryblokComponent blok={story.content} />
      </div>
    )
  }

...
Enter fullscreen mode Exit fullscreen mode

That's it!

Now, when NextJS builds your page, it will only include the components imported directly on that page and reduce your JS bundle size per page, improving your SEO and speeding up your page load times.

Comments 2 total

  • Kozlov Sergey
    Kozlov SergeyFeb 14, 2024

    Hey! Nice point!
    I'll just try to improve your solution a little.
    In your case, react will call setComponents function for each render. It's fair that this can rarely happen (depending on your logic). But still it can be.
    Therefore, you can make a primitive hook so that the call occurs only once on the first render:

    import { useState } from 'react';
    import { SbReactComponentsMap, setComponents } from '@storyblok/react';
    
    export function useComponents(components: SbReactComponentsMap) {
      useState(() => setComponents(components));
    }
    
    Enter fullscreen mode Exit fullscreen mode
    • Dušan Perković
      Dušan PerkovićFeb 28, 2024

      Hey Kozlov, thanks for the reply! Appreciate the contribution :)

      If I understand you coorectly, I'm guessing we could achieve a similar thing by wrapping the function with useCallback?

Add comment