Basic HTML + JSON Tutorial

Learn the basics of Stackbit with a basic HTML site using JSON files to store content.

This tutorial teaches the basics of Stackbit without the need to know any particular framework or content source.

The example project has a single script that feeds JSON files into EJS templates to generate static HTML pages. You can read more about how the project works in its README.

Project Setup

Let's begin by getting the example project setup on your local machine.

Prerequisites

  • Development machine that can run Node v14 or newer

Clone Example Project

Use create-stackbit-app to clone the example project and install dependencies.

  • 1
npx create-stackbit-app@latest --example tutorial-html-json

Change into the project directory when installation has completed. Unless otherwise noted, all commands will be run from the project root.

  • 1
cd tutorial-html-json

Run the Website

Start the development server and view the site at localhost:3000.

  • 1
npm run dev
Next.js Development Site
Next.js Development Site

Stackbit Configuration

Adding Stackbit's local visual editor application to an existing site takes just a few quick steps.

Install CLI

Install the Stackbit CLI, which we'll use to launch the visual editor.

  • 1
npm install -g @stackbit/cli@latest

Run Visual Editor

With the development server still running on port 3000, open a new terminal session to run the visual editor using the CLI's dev command.

  • 1
stackbit dev

Now, if you visit localhost:8090, you'll see the example project that is running on port 3000. The application running on port 8090 is a local Stackbit application that proxies to your development server, and also contains a few assets and routes to facilitate visual editing.

Register Your Project

One such route is /_stackbit. This will redirect to the authentication process that makes it possible to work with Stackbit locally.

Open localhost:8090/_stackbit in your browser and create an account. You'll be redirected to a new URL that is unique to your local editing environment. The preview you see here is the application running on localhost:8090.

Stackbit Visual Editor
Stackbit Visual Editor

Configure Content Source

Next, we have to add our configuration file to tell Stackbit how content is stored. To help with this process, we'll install the Git CMS module, along with our types library. These are both development dependencies — Stackbit does not require adding any code to production.

  • 1
npm install -D @stackbit/types @stackbit/cms-git

Then, add a stackbit.config.ts configuration file that specifies the source of content as Git CMS (or file-based content). This minimal configuration will get us started working locally.

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

export default defineStackbitConfig({
  stackbitVersion: '~0.6.0',
  contentSources: [
    new GitContentSource({
      rootPath: __dirname,
      contentDirs: ['src/pages'],
      models: [],
    }),
  ],
})

Basic Content Editing

When working with file-based content, Stackbit can't infer the content schema like it can when using a structured content source. Therefore, we also have to tell Stackbit about the shape of our content.

In this simple project, we'll use three models — page, heading, and paragraph — where page represents the site's pages and has a sections field that can accept heading or paragraph content.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • 35
  • 36
  • 37
  • 38
import { defineStackbitConfig } from '@stackbit/types'
import { GitContentSource } from '@stackbit/cms-git'

export default defineStackbitConfig({
  stackbitVersion: '~0.6.0',
  contentSources: [
    new GitContentSource({
      rootPath: __dirname,
      contentDirs: ['src/pages'],
      models: [
        {
          name: 'page',
          type: 'page',
          hideContent: true,
          fields: [
            { name: 'title', type: 'string', required: true },
            { name: 'sections', type: 'list', items: { type: 'model', models: ['paragraph', 'heading'] } },
          ],
        },
        {
          name: 'paragraph',
          type: 'object',
          labelField: 'content',
          fields: [{ name: 'content', type: 'markdown', required: true, default: '' }],
        },
        {
          name: 'heading',
          type: 'object',
          labelField: 'content',
          fields: [
            { name: 'content', type: 'string', required: true },
            { name: 'level', type: 'enum', required: true, options: [1, 2, 3, 4, 5, 6] },
          ],
        },
      ],
    }),
  ],
})

Now Stackbit knows how to find pages by looking in the directory specified by the pagesDir property. As a result, the content tab is populated with the two pages in the project. Editing will write the updates to the local file.

Basic Content Editing Panel
Basic Content Editing Panel

Page Editing

Pages are a type of model that represent a single URL path in your site.

When the proper configuration, Stackbit can build a complete sitemap and show the proper content fields as editors navigate between pages in the application. This requires specifying which models represent pages and how each page maps to a URL path in the site.

Specifying Page Models

We've already specified the page model to be of type page, but we also need to tell Stackbit how to map page documents to URLs within the site, and where to put new pages when editors create them.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • +
  • +
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
import { defineStackbitConfig } from '@stackbit/types'
import { GitContentSource } from '@stackbit/cms-git'

export default defineStackbitConfig({
  stackbitVersion: '~0.6.0',
  contentSources: [
    new GitContentSource({
      rootPath: __dirname,
      contentDirs: ['src/pages'],
      models: [
        {
          name: 'page',
          type: 'page',
          urlPath: '/{slug}',
          filePath: 'src/pages/{slug}.json',
          hideContent: true,
          fields: [
            { name: 'title', type: 'string', required: true },
            { name: 'sections', type: 'list', items: { type: 'model', models: ['paragraph', 'heading'] } },
          ],
        },
        {
          name: 'paragraph',
          type: 'object',
          labelField: 'content',
          fields: [{ name: 'content', type: 'markdown', required: true, default: '' }],
        },
        {
          name: 'heading',
          type: 'object',
          labelField: 'content',
          fields: [
            { name: 'content', type: 'string', required: true },
            { name: 'level', type: 'enum', required: true, options: [1, 2, 3, 4, 5, 6] },
          ],
        },
      ],
    }),
  ],
})

Sitemap Navigator

Going back to the Stackbit editor, you should now see the sitemap populated with the home page entry, which was added to the CMS when importing content.

Sitemap Navigator with Home Page
Sitemap Navigator with Home Page

Contextual Page Editor

This change also enabled the contextual page editor (pencil icon in left sidebar). Notice that you can open this panel and see the fields and values for page visible in the preview. And if you navigate to the other page, the fields change accordingly.

Contextual Page Editor
Contextual Page Editor

Inline Editing

The most advanced (and productive) form of editing with Stackbit is inline editing, which is made possible through the use of annotations.

These are simple HTML data attributes (data-sb-object-id and data-sb-field-path) that map content in Contentful to elements on the page. This enables editors to click directly in the preview, make a change, and see that change reflected in Contentful.

Set the Object ID

The first step is to declare the ID of the content object using a data-sb-object-id attribute. This points Stackbit to the origin of the content on the screen.

When working with local files, the object ID should be the path to the file, relative to the root of the project. Conveniently, the templating script in the project already makes this available in a _meta.id property on each page.

Add the data-sb-object-id attribute to the top-level wrapper in the default layout.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
<body>
  <main data-sb-object-id="<%- page._meta.id %>">
    <% (page.sections || []).forEach((section, index) => { %>
    <div><%- component(section) %></div>
    <% }) %>
  </main>
</body>

This makes any element within <main> automatically associated with the current page.

Add the Field Paths

Now we can add a series of field path attributes to annotate additional components. Field paths use dot notation to drill down into a particular field. For example, if this is how a page is structured.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
{
  "title": "Home Page",
  "type": "page",
  "layout": "default",
  "sections": [
    {
      "type": "heading",
      "content": "Home Page",
      "level": 1
    }
  ]
}

The field path to content within the first section would be sections.0.content.

Therefore, we'll wrap the sections using an iterator to denote the current index in the sections array, and then each component will hard-code it's own fields.

  • 1
  • 2
  • 3
  • 4
  • 5
<main data-sb-object-id="<%- page._meta.id %>">
  <% (page.sections || []).forEach((section, index) => { %>
  <div data-sb-field-path="sections.<%- index %>"><%- component(section) %></div>
  <% }) %>
</main>

Now inline editing will work! You can hover over an element on the page to highlight and change it.

Update Heading Inline
Update Heading Inline

Additional Capabilities

You have now added visual editing to a simple site with Stackbit! But this is just the beginning. You can expand on what you learned above, or explore additional ways to extend the visual editing experience for your site.

This is typically what developers explore next:

And as you progress, there are more advanced features to consider: