Publishing a Widget as an NPM Package

Have you created a Scrivito widget you would like to share with other developers for adding it to their Scrivito-based app? Or maybe you’ve written a cool integration with a third-party service, and want to spare others the effort.

In this guide we are going to explain to you the basic procedure for creating an npm package from a Scrivito widget. We’ll use a slightly simplified version of our FaqWidget here.

Develop your widget

The first thing you’ll need is, of course, the widget you want to share. So, develop your widget as part of your app, which might or might not be based on the Scrivito Example App.

Initialize your package project

If you want to share the code of your future package on a repository hosting platform such as GitHub or BitBucket, create a repository and clone it to your local machine. The resulting directory would be your project directory for the package. Otherwise, create the project directory manually, named like your package.

Make sure you have npm on your machine, then change to your project directory and run npm init. You will be asked to enter the package name, the URL of your remote repository (e.g. on GitHub), and a couple of things more.

Install the dependencies

Next, install the development dependencies:

Copy
npm install -D webpack webpack-cli @babel/core @babel/plugin-proposal-object-rest-spread @babel/plugin-transform-react-jsx @babel/preset-env babel-loader

Your “package.json” file should now look somewhat like this:

package.json
Copy
{
  "name": "scrivito-faq-widget",
  "version": "1.0.0",
  "description": "A question/answer widget for a Scrivito app",
  "main": "build/index.js",
  "scripts": {
    "start": "webpack --watch",
    "build": "webpack"
  },
  "peerDependencies": {
    "react": ">=16.4.0",
    "scrivito": ">=1.3.0"
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com/username/scrivito-faq-widget.git"
  },
  "keywords": [
    "scrivito",
    "widget"
  ],
  "author": "username",
  "license": "LGPL-3.0-or-later",
  "bugs": {
    "url": "https://github.com/username/scrivito-faq-widget/issues"
  },
  "homepage": "https://github.com/username/scrivito-faq-widget#readme",
  "devDependencies": {
    "@babel/core": "^7.1.5",
    "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
    "@babel/plugin-transform-react-jsx": "^7.0.0",
    "@babel/preset-env": "^7.1.5",
    "babel-loader": "^8.0.4",
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2"
  }
}

Since the widget requires the host app to have React and Scrivito installed, we’ve added them as peer dependencies to this configuration. We’ve also changed the entry point (main) to “build/index.js”, and added the start and build script names.

If your widget depends on other modules, don’t forget to install them (and add them to the dependencies) as needed.

In case you need to upgrade babel, there’s a tool for this that makes things a lot easier, babel-upgrade.

Configure webpack

For having webpack build your package and transpile your React code to JavaScript, two files are required in the project directory, “webpack.config.js” and “.babelrc”:

webpack.config.js
Copy
var path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'index.js',
    libraryTarget: 'umd'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/env']
          }
        }
      }
    ]
  },
  externals: ['react', 'scrivito'],
  mode: 'production'
};
.babelrc
Copy
{
    "presets": [
        "@babel/preset-env"
    ],
    "plugins": [
        "@babel/plugin-proposal-object-rest-spread",
        "@babel/plugin-transform-react-jsx"
    ]
}

Add your widget

In your project directory, create a folder named “src”. In this folder, create a file named “index.js”. This file is the entry point, meaning that Webpack will execute the code in the process of installing your package.

You can place all the code your widget is comprised of in this “index.js” file, or import parts from separate files. In the case of an ordinary widget, you’d copy-paste the contents of the “…Class.js”, “…EditingConfig.js”, and “…Component.js” files like we did below for our FaqWidget

src/index.js
Copy
import * as React from 'react';
import * as Scrivito from 'scrivito';

const FaqWidget = Scrivito.provideWidgetClass('FaqWidget', {
  attributes: {
    question: 'string',
    answer: 'html',
  },
});

Scrivito.provideEditingConfig('FaqWidget', {
  title: 'FaqWidget',
  description: 'A frequently asked question.',

  attributes: {
    question: {
      title: 'Question',
      description: 'State the frequently asked question',
    },
    answer: {
      title: 'Answer',
      description: 'Provide an eloquent answer',
    },
  },

  properties: ['question', 'answer'],
  initialContent: {
    question: "What is the question?",
    answer: "And this is the answer.",
  },
});

Scrivito.provideComponent('FaqWidget', ({ widget }) => {
  return (
    <div className='mt-2 mr-0 pt-0 pr-0 pb-3 pl-2 border-bottom'>
      <Scrivito.ContentTag tag='h3' content={ widget } attribute='question' />
      <Scrivito.ContentTag content={ widget } attribute='answer'/>
    </div>
  );
});

export default FaqWidget;

Note that, to keep it simple, we’ve replaced the CSS file used in the FaqWidget tutorial with Bootstrap classes.

Build and test your package

Now, let’s build the package by executing:

Copy
npm install
npm run build

To test and use the package locally, link it to its dependencies using:

Copy
npm link

Then switch to the project directory of the Scrivito-based app in which you wish to test the package and run (replace the package name with yours):

Copy
npm link scrivito-faq-widget
npm install

Finally, start your test app and use the functionality you provided, in this case by importing it into your app’s “src/index.js”, like so:

src/index.js (of your test app)
Copy
// Other imports
import FaqWidget from "scrivito-faq-widget";

// ...

When adding a widget to a page, the “FaqWidget” should now be among the widgets offered!

Publish your package

If everything works as it should and your readme file is complete, sign up for an npm account, if you don’t have one already, and make sure that your package name is unique. Then npm publish. If you want to test-publish the package locally, there’s an npm sibling for this, Verdaccio.

That was it. You should now be able to offer widgets as npm packages!