Adding Scrivito to Create-React-App

Facebook’s create-react-app offers an easy and well-documented way of bootstrapping a React app. In this tutorial, we will show you how to add Scrivito to such an app, allowing you to freely combine Scrivito-CMS-managed content with whatever content you wish to deliver separately.

You require Node >= 6, npm >= 5.2, and git for the instructions in this tutorial to work as intended. Needless to say that you need a Scrivito CMS, too.

Create the React app and initialize the repository

Let’s start! In a terminal, change to where your projects are, then create the React app and initialize the repository:

Copy
npx create-react-app --use-npm react-scrivito
cd react-scrivito
git init
git add .
git commit -m "Initial commit"

Add Scrivito and its basic configuration, app code and markup

Copy
npm i --save scrivito
git add .
git commit -m "Added the scrivito module"

Now create Scrivito’s configuration file, “src/config/scrivito.js” and set its contents to:

src/config/scrivito.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.configure({
  tenant: 'YOUR-CMS-TENANT-ID',
});

Replace YOUR-CMS-TENANT-ID with exactly that. Please find the tenant ID of your Scrivito website in your dashboard on the “Settings” tab.

USING ENVIRONMENT VARIABLES WITH CREATE-REACT-APP

Using environment variables is always a safer option compared to hard-coding sensitive data. When using the Create React App Webpack settings, one must prefix any variable with REACT_APP_ for it to be picked up. For this project, the call to Scrivito.configure would look like this:

Copy
// src/config/scrivito.js

import * as Scrivito from 'scrivito';

Scrivito.configure({
  tenant: process.env.REACT_APP_SCRIVITO_TENANT,
});

Then add the variable to the “.env” file in your project and on Netlify in your “Build & deploy” settings. Finally, don't forget to add “.env” to your “.gitignore” file.

Then make “src/index.js” look like this:

src/index.js
Copy
import React from 'react';
import ReactDOM from 'react-dom';
import './config/scrivito';
import './Objs';
import './Widgets';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

In addition to the configuration we provided above, the (not yet existing) CMS object and widget class definitions are imported. We’ll provide a couple of basic CMS classes later on.

What you render here, in this case via the “App” component, depends on where you started off. For a fresh React app without any relevant content (like ours), it makes sense to render just the Scrivito-based content, while a fully fledged React app probably requires some kind of routing. There’s a dedicated tutorial on implementing routing using React Router.

To have the app render the current (Scrivito-based) page (in addition to the default header), change “src/App.js” to the following:

src/App.js
Copy
import React, { Component } from 'react';
import * as Scrivito from 'scrivito';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React with Scrivito</h1>
        </header>
        <Scrivito.CurrentPage />
      </div>
    );
  }
}

export default App;

Commit your changes:

Copy
git add .
git commit -m 'Added config and basic app code'

Generate and finalize the webpack configuration

First, generate the webpack configuration:

Copy
npm run eject

In a standard Scrivito-based app the editing interface is available under the “/scrivito” URL. To make this work here, we will instruct webpack to copy the corresponding HTML file included in the SDK (“index.html”) to somewhere else and make “/scrivito” point to this file. We require the copy-webpack-plugin for copying the file, so first run:

Copy
npm i --save copy-webpack-plugin

Then we use the plugin in the webpack configuration, “webpack.config.dev.js”, to actually provide a copy of the UI-enabled “index.html” file:

config/webpack.config.dev.js
Copy
// Other definitions
const CopyWebpackPlugin = require('copy-webpack-plugin');

// …
plugins: [
    new CopyWebpackPlugin([
      { from: './node_modules/scrivito/scrivito/index.html', to: 'scrivito/index.html' },
    ]),
    // More plugins
  ],
// …
}

Finally, to let the web server know where to expect this “index.html” file, add the corresponding rewrites to the historyApiFallback key in “config/webpackDevServer.config.js”:

config/webpackDevServer.config.js
Copy
// …

    historyApiFallback: {
      // Paths with dots should still use the history fallback.
      // See https://github.com/facebookincubator/create-react-app/issues/387.
      disableDotRule: true,
      rewrites: [
        { from: /^\/scrivito$/, to: '/scrivito/index.html' },
        { from: /^\/scrivito\//, to: '/scrivito/index.html' },
        { from: /./, to: '/index.html' },
      ],
    },

Provide some page and widget classes and components

To be able to create pages and widgets, we require Scrivito CMS object and widget classes. Assuming you have a fresh Scrivito CMS with the sample content for the Example Application, all we need to get started are a “Homepage” and an “Image” object class as well as two widget classes; “SectionWidget” and “TextWidget”. These classes will suffice to display at least a small portion of the homepage content.

Our “src/index.js” file above imports object classes from “src/Objs” and widget classes from “src/Widgets”. After creating these directories, we will add the following “index.js” file to each of them to have the contents of the class subdirectories imported automatically.

Alternatively, you can download the files as an archive and uncompress it to the “src” directory if you don’t want to create them all manually. The download includes a Page object class and an Image widget class in addition to the examples below, as these are commonly used.

index.js (in src/Objs/ and src/Widgets/)
Copy
function importAll(r) {
  r.keys().forEach(r);
}

// Import all js files from subdirectories
importAll(require.context('./', true, /\.js$/));

Homepage

src/Objs/Homepage/HomepageObjClass.js
Copy
import * as Scrivito from 'scrivito';

const Homepage = Scrivito.provideObjClass('Homepage', {
  attributes: {
    body: 'widgetlist',
  },
});

export default Homepage;
src/Objs/Homepage/HomepageComponent.js
Copy
import * as React from 'react';
import * as Scrivito from 'scrivito';

Scrivito.provideComponent('Homepage', ({ page }) =>
  <Scrivito.ContentTag tag="div" content={ page } attribute="body" />
);

Image

src/Objs/Image/ImageObjClass.js
Copy
import * as Scrivito from 'scrivito';

const Image = Scrivito.provideObjClass('Image', {
  attributes: {
    blob: 'binary',
  },
});

export default Image;

SectionWidget

src/Widgets/SectionWidget/SectionWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

const SectionWidget = Scrivito.provideWidgetClass('SectionWidget', {
  attributes: {
    content: 'widgetlist',
  },
});

export default SectionWidget;
src/Widgets/SectionWidget/SectionWidgetComponent.js
Copy
import * as React from 'react';
import * as Scrivito from 'scrivito';

Scrivito.provideComponent('SectionWidget', ({ widget }) => {
  return (
    <Scrivito.ContentTag content={ widget } attribute="content" />
  );
});

TextWidget

src/Widgets/TextWidget/TextWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

const TextWidget = Scrivito.provideWidgetClass('TextWidget', {
  attributes: {
    text: 'html',
  },
});

export default TextWidget;
src/Widgets/TextWidget/TextWidgetComponent.js
Copy
import * as React from 'react';
import * as Scrivito from 'scrivito';

Scrivito.provideComponent('TextWidget', ({ widget }) => {
  return (
    <Scrivito.ContentTag
      tag="div"
      content={ widget }
      attribute="text"
    />
  );
});

Fire up your app!

In the terminal, from the project directory of your app, run:

Copy
npm install
npm start

This opens http://localhost:3000 in your browser. If you specified the ID of a new Scrivito CMS in “src/config/scrivito.js”, or haven’t used this URL before, you will need to authorize it on the “Deployment” tab of the associated CMS tenant's settings.

If your CMS content includes a page object for the “/” URL and at least a text widget (like the example app does), you should see a page with some text. This is the published content, without the Scrivito UI:

Blind text on the homepage (UI not loaded)

To have the Scrivito UI displayed, add ”/scrivito” to the URL: http://localhost:3000/scrivito:

Homepage (UI loaded, published content)

Remember that we defined only two widget classes (section and text), so only content from widgets of these types will be displayed. Now, click “Edit” in the top panel; you will see errors from all the other (not defined) widgets on the page:

Homepage (editing mode)

The missing widget classes causing the “application error” messages are included in the Example App you could install to transfer and adapt the classes as needed. As this tutorial is based on the idea of starting from scratch, you might want to delete the sample content from your Scrivito CMS.

Final words

You now have Scrivito integrated with Facebook's create-react-app and can start developing your app further. Add page and widget classes for handling Scrivito-based content, include some styling, and implement routing if you plan to deliver mixed-source content. If you are new to Scrivito, take a look at the other guides and tutorials we have available.

This tutorial was written using React 15.6, Webpack 4 and Scrivito SDK 1.3. The directions should work without change with all minor versions going forward, but let us know if you encounter any issue.