Custom Content Sources

An introduction to using the Content Source Interface (CSI)

Early Access Feature

Content Source Interface is in early access and subject to breaking changes. To be informed of changes, contact us.

Content Source Interface (CSI) provides a two-way synching mechanism between Stackbit's visual editor and your content source(s) (API-based CMS, remote database, files, etc.).

CSI defines methods to load the content and its schema from the underlying content source, perform CRUD operations (create/read/update/delete) on the content, upload assets, and other content-related operations.

Some modules are developed & supported by Stackbit, but you can build your own content sources as well.

Example Usage

Enabling this feature for any content source requires instantiating a module within the contentSources config property. Here's an example using Contentful.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
import { ContentfulContentSource } from '@stackbit/cms-contentful'

export default {
  contentSources: [
    new ContentfulContentSource({
      spaceId: process.env.CONTENTFUL_SPACE_ID,
      environment: process.env.CONTENTFUL_ENVIRONMENT,
      previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN,
      accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN,
    }),
  ],
  // ...
}

If configured properly, you will immediately be able to see your models in the content panel.

List of Content Models
List of Content Models

You can then drill into any object and see two-way sync in action. Edit it in Stackbit, and the source gets updated. Edit it in the source, and within moments Stackbit will show the updated value.

Page Editing in Content Panel
Page Editing in Content Panel

CSI and Your Code

CSI modules and the stackbit.config.js file are not loaded by your site's code. Rather, these are used by Stackbit's dev server to load and modify data through the visual editor.

Your own code for fetching data from any of its content sources is not impacted or replaced by the CSI, at all. This means that:

  • You are still responsible for writing the code that retrieves content from your source(s) and feeds them into your pages and components.
  • You don't have to make changes to your site to be able to use CSI (assuming that your content is not hard-coded into your code)
  • Any dependencies needed for CSI can be installed as development dependencies, and are not needed for your live site.

Supported Sources

The following modules are supported directly by Stackbit:

Experimental Sources

We're currently working on these additional sources:

For inquiries and requests regarding new sources, please contact us.

Using a Content Source

Working with a supported content source takes just a few steps. Assuming you already have the source itself (e.g. the CMS) ready to go, the process usually looks like this:

  1. Install the NPM package for the module (as a development dependency).
  2. Add the content source to the contentSources array in your configuration file.

Here's an example that adds Contentful as a content source.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
import { ContentfulContentSource } from '@stackbit/cms-contentful'

export default {
  contentSources: [
    new ContentfulContentSource({
      spaceId: process.env.CONTENTFUL_SPACE_ID,
      environment: process.env.CONTENTFUL_ENVIRONMENT,
      previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN,
      accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN,
    }),
  ],
  // ...
}

Adding Multiple Sources

Because contentSources is an array of instantiated classes, adding another source is a matter of adding a new item to the array.

This means that using two accounts from the same service provider is also possible. Here's an example:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
import { ContentfulContentSource } from '@stackbit/cms-contentful'

export default {
  contentSources: [
    new ContentfulContentSource({
      spaceId: process.env.CONTENTFUL_SPACE_ID_01,
      environment: process.env.CONTENTFUL_ENVIRONMENT_01,
      previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN_01,
      accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN_01,
    }),
    new ContentfulContentSource({
      spaceId: process.env.CONTENTFUL_SPACE_ID_02,
      environment: process.env.CONTENTFUL_ENVIRONMENT_02,
      previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN_02,
      accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN_02,
    }),
  ],
  // ...
}

Bringing Your Own Source

Because content source modules are defined as instantiated JavaScript classes in the configuration file, you can bring your own content source, even if not officially supported by Stackbit.

Use the CSI API reference to ensure you're implementing the JavaScript class properly.

Module Requirements

Any content source that can read and write data via an API or direct local access can be used as a content module. This includes:

  • Headless (or API-based) CMS (e.g. Contentful)
  • Database as a Service (e.g. PlanetScale)
  • Internal product database (e.g. PostgreSQL)
  • Non-traditional services (e.g. Figma)
  • File-based content (e.g. Markdown files)

Using TypeScript

We recommend using TypeScript in your CSI module. This benefits both you by ensuring that you implement all necessary methods. It also benefits developers that using the module by making it easier to discover required options when instantiating the class in their configuration file.

Migrating to a New Source

CSI makes it trivial to migrate to a new content source without sacrificing content downtime or adding complexity to your code.

You can move one page at a time from the old source to the new source as long as:

  • You have properly configured the content sources, and ...
  • Your front-end code supports the content structure from both sources.

Configuration Example

Ensure that you're adding both sources to your config file:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
import { MyOldSource } from 'my-old-source'
import { MyNewSource } from 'my-new-source'

export default {
  contentSources: [
    new MyOldSource({
      // configuration options ...
    }),
    new MyNewSource({
      // configuration options ...
    }),
  ],
  // ...
}

Then you can add a new page to the new source and delete the old page from the old source. Do this one at a time until you've moved all the content to the new content source.