This plugin allows consuming content loaded by any of the Sourcebit source plugins (e.g., sourcebit-source-filesystem, sourcebit-source-contentful , sourcebit-source-sanity) inside Next.js pages using getStaticProps and getStaticPaths methods.
- When Next.js starts, it loads Sourcebit and runs its fetch()method that executes Sourcebit plugins.
- A Sourcebit source plugin loads the content from a CMS or a file-system.
- The sourcebit-target-nextplugin caches the loaded content and saves it inside the.sourcebit-nextjs-cache.jsonfile.
- When Next.js renders a page, it calls getStaticProps and getStaticPaths methods. These methods call
the sourcebitDataClient.getData()method to load the content cached inside the.sourcebit-nextjs-cache.jsonfile and reduce it according to the needs of a particular page.
- 
Install the main sourcebitpackage, thissourcebit-target-nextplugin, and one of the Sourcebit "source" plugins to fetch your content (e.g., sourcebit-source-filesystem, sourcebit-source-contentful, sourcebit-source-sanity, or create your own).npm install sourcebit sourcebit-target-next sourcebit-source-filesystem
- 
Create sourcebit.jsconfiguration file in the root of your Next.js project:const path = require('path'); const isDev = process.env.NODE_ENV === 'development'; module.exports = { plugins: [ /** * The `sourcebit-source-filesystem` plugin reads content files from the provided `options.sources` folders, * and generates an array of objects that is passed to the subsequent plugins. */ { module: require('sourcebit-source-filesystem'), options: { watch: isDev, sources: [ { name: 'pages', path: path.join(__dirname, 'content/pages') }, { name: 'data', path: path.join(__dirname, 'content/data') } ] } }, /** * If needed, add more plugins to transform the data */ ({ data }) => { return { ...data, objects: data.objects.map((object) => { // ... trasnform the objects return object; }) }; }, /** * The `sourcebit-target-next` plugin receives the data generated by `sourcebit-source-filesystem` plugin, * and stores it in `.sourcebit-nextjs-cache.json` file. */ { module: require('sourcebit-target-next'), options: { liveUpdate: isDev, flattenAssetUrls: true } } ] }; 
- 
Wrap the config exported in next.config.jsin the following way:const withSourcebit = require('sourcebit').sourcebitNext(); module.exports = withSourcebit({ // ... next.js config ... }); The withSourcebitfunction loads thesourcebit.jsconfig file and runssourcebit.fetch(config)method for you, before starting the Next.js server. If you want to control the process yourself, you can load the sourcebit config and callfetch()manually:const sourcebit = require('sourcebit'); const sourcebitConfig = require('./sourcebit.js'); sourcebit.fetch(sourcebitConfig); 
- 
To consume data fetched by Sourcebit and cached by sourcebit-target-next, import thesourcebitDataClientfromsourcebit-target-nextand call itsgetData()method insidegetStaticPathsandgetStaticPropsmethods. You can use one of the__metadataproperties added bysourcebit-source-filesystemto generate props needed for a particular page.- 
If a page does not use dynamic routes, then it should call the getData()method fromgetStaticPropsto load the cached data and compute the props specific to that page:// src/pages/about.js import { sourcebitDataClient } from 'sourcebit-target-next'; export async function getStaticProps() { const data = await sourcebitDataClient.getData(); const props = data.objects.find((object) => object?.__metadata?.relSourcePath === 'about.md') return { props }; } 
- 
If a page does use dynamic routes, e.g., src/pages/[[...slug]].js, then it should have both thegetStaticPathsand thegetStaticPropsmethods. UsegetData()ingetStaticPathsto generate paths for all your pages, and then ingetStaticPropsto compute props for a particular page according to the slug parameter.// src/pages/[[...slug]].js import { sourcebitDataClient } from 'sourcebit-target-next'; export async function getStaticPaths() { const data = await sourcebitDataClient.getData(); const paths = data.objects // find objects loaded from the "pages" folder (__metadata.sourceName === "pages") .filter((object) => object?.__metadata?.sourceName === 'pages') // map the file path to the page url, e.g.: 'about.md' => '/about' .map((object) => urlPathFromFilePath(object?.__metadata?.relSourcePath)) return { paths }; } export async function getStaticProps() { const urlPath = '/' + (params?.slug || []).join('/'); const data = await sourcebitDataClient.getData(); // find the objects corresponding to the requested page const props = data.objects.find((object) => urlPathFromFilePath(object?.__metadata?.relSourcePath) === urlPath); return { props }; } 
 
- 
- 
When working locally, you might want the browser to automatically reflect any content changes. To do that, wrap your pages with the following higher order component: import { withRemoteDataUpdates } from 'sourcebit-target-next/with-remote-data-updates'; class Page extends React.Component { render() { // ... } } export default withRemoteDataUpdates(Page); 
This plugin can be configured with several options:
// sourcebit.js
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
    plugins: [
        // ...otherPlugins,
        {
            module: require('sourcebit-target-next'),
            options: {
                liveUpdate: isDev,
                cacheFilePath: path.join(__dirname, '.custom-filename.json'),
                disableCache: false,
                flattenAssetUrls: true
            }
        }
    ]
};- liveUpdate(boolean) A flag indicating if page should reload its data when the underlying content changes. Defaults to- truewhen- NODE_ENVis set to- development.
- cacheFilePath(string) A file path for cached data, default is- .sourcebit-nextjs-cache.json
- disableCache(boolean) A flag indicating if plugin should produce the cache file. Note, if you set this flag to true you won't be able to use- sourcebitDataClientto consume the data and will have to call- sourcebit.fetch()for every page.
- flattenAssetUrls(boolean) Some source plugins might return complex objects for linked assets. Set this flag to- trueto replace these objects with a string representing the absolute asset URL.
You can check out an example project that uses
sourcebit-source-sanity and sourcebit-target-next plugins to fetch the data from Sanity.io
and consume it inside Next.js pages.
Add following to your .gitignore:
.sourcebit-nextjs-cache.json
To simplify the dynamic routing architecture and to allow greater flexibility when creating pages in Headless CMS, we advise using following pattern:
pages/[[...slug]].js
import React from 'react';
import { sourcebitDataClient } from 'sourcebit-target-next';
import { withRemoteDataUpdates } from 'sourcebit-target-next/with-remote-data-updates';
import pageLayouts from '../layouts';
class Page extends React.Component {
    render() {
        // every page can have different layout, pick the layout based on the "layout" property of the page
        const PageLayout = pageLayouts[_.get(this.props, 'page.layout')];
        return <PageLayout {...this.props} />;
    }
}
export default withRemoteDataUpdates(Page);
export async function getStaticPaths() {
    const data = await sourcebitDataClient.getData();
    const paths = data.objects
        // find objects loaded from the "pages" folder (__metadata.sourceName === "pages")
        .filter((object) => object?.__metadata?.sourceName === 'pages')
        // map the file path to the page url, e.g.: 'about.md' => '/about'
        .map((object) => urlPathFromFilePath(object?.__metadata?.relSourcePath))
    return {
        paths,
        fallback: false
    };
}
export async function getStaticProps({ params }) {
    const urlPath = '/' + (params?.slug || []).join('/');
    const data = await sourcebitDataClient.getData();
    // find the objects corresponding to the requested page
    const props = data.objects.find((object) => urlPathFromFilePath(object?.__metadata?.relSourcePath) === urlPath);
    return { props };
}