Quick Development with nwb
May 16, 2020 · View on GitHub
Prerequisite: nwb must be installed globally (we're using version 0.18 in this guide):
npm install -g nwb
Quick development commands reduce the time between having a flash of inspiration and being able to develop and share it.
They are:
nwb reactfor React codenwb infernofor Inferno codenwb preactfor Preact codenwb webfor vanilla JavaScript code
They all have the same sub-commands:
run <entry.js>runs a development serverbuild <entry.js> [dist/]creates a production build
Example
Say you have an idea for a Preact component and you whip up Lightbulb.js:
import {h, Component} from 'preact'
export default class Lightbulb extends Component {
state = {
on: false
}
updateChecked = (e) => {
this.setState({on: e.target.checked})
}
render({wattage = 200}, {on}) {
return <div>
<label>
<input type="checkbox" checked={on} onClick={this.updateChecked}/>
{' '}
{wattage}W lightbulb is {on ? 'on' : 'off'}
</label>
</div>
}
}
To serve this for development use nwb preact run, which will install essential dependencies, start a Webpack development server and automatically re-render when you make changes:
$ nwb preact run Lightbulb.js
✔ Installing preact
Starting Webpack compilation...
Compiled successfully in 3717 ms.
The app is running at http://localhost:3000/
To create a production build, use nwb preact build:
$ nwb preact build Lightbulb.js
✔ Building Preact app
File size after gzip:
dist\app.b12334ec.js 8.63 KB
Table of Contents
- Options for
runandbuildcommands - Features
- Configuration
Options for run and build commands
Shared options
-c, --config
Specify a configuration file to use - see Configuration File.
-p, --plugins
Specify nwb plugins to use - see Style Preprocessing.
--force
Don't use a rendering shim, run the provided entry module directly - see Opting out of Rendering Shims.
--mount-id
The default HTML template provided contains a <div> which the app is rendered into, with a default id of 'app'. You can change the id with this setting.
--title
Contents for <title> - defaults to the type of app you're serving, e.g. 'Preact App'
run Options
--install
Use NpmInstallPlugin to automatically install missing dependencies when an attempt is made to import them - see Automatic Dependency Installation.
--host
Configures the host the development server will bind to.
--no-clear
Disables clearing the console before displaying Webpack compilation results - nwb clears the console by default to make it easier to read error messages without having to scroll back.
--no-fallback
Disables the default fallback serving of index.html for apps which make use of the HTML5 History API.
--port
Port to run the dev server on (default: 3000)
--reload
Automatically refresh the page when a Hot Module Replacement request was not accepted.
Note:
nwb web rundefaults this setting totruewill reload the page by default on changes. To disable this, use a--no-reloadflag or use Webpack's Hot Module Replacement API in your app to accept module updates.
build Options
--vendor
Enable extraction of modules imported from node_modules/ into a separate vendor bundle.
--inferno[-compat]
--preact[-compat]
These options only apply to
nwb react build
Create a build of a React project which uses Inferno or Preact as the runtime via a compatibility layer - see React Compatible Builds.
Features
Zero Configuration Setup
-
Write JavaScript with modern features and JSX, transpiled down to ES5.
-
Use new JavaScript feature proposals in the TC39 process:
- Class properties, for avoiding boilerplate when writing classes.
- Decorators.
- Export extensions.
-
Import images and stylesheets into JavaScript like any other module, to be handled by Webpack as part of its build.
import 'bootstrap/dist/css/bootstrap.css' <img src={require('./picture.jpg')}/> -
Autoprefixed CSS, so you don't need to write browser prefixes.
-
Fallback serving of
index.htmlat any URL so apps which use the HTML5 History API will work by default.
Style Preprocessing
nwb has plugin modules which add Sass (nwb-sass), Less (nwb-less) and Stylus (nwb-stylus) support without needing configuration.
To use them, pass a --plugins option with an nwb plugin name (or a comma-separated list of names), with or without its nwb- prefix.
e.g. to support importing Sass stylesheets, pass --plugins sass and the necessary plugin will automatically be installed and used:
$ nwb preact run Lightbulb.js --plugins sass
✔ Installing nwb-sass
Starting Webpack compilation...
Automatic Dependency Installation
Having to stop and restart your development server to install new dependencies becomes a minor annoyance when you're in the middle of writing code.
If you pass an --install flag to the run command, nwb will configure NpmInstallPlugin to automatically install missing dependencies when an attempt is made to import them.
$ nwb preact run Lightbulb.js --install
Starting Webpack compilation...
Now if we add the following to the top of Lightbulb.js:
import 'bootstrap/dist/css/bootstrap.css'
Bootstrap will be installed and applied to the running app:
Recompiling...
Installing bootstrap...
Making use of React Alternatives' Compatibility with React
Inferno and Preact both provide compatibility layers which simulate React APIs and patch up features which are different or missing compared to React, to allow them to run existing React code.
React Compatible Builds with --inferno or --preact
To try this out with some React code you've written, you can pass an --inferno flag for a build which uses inferno-compat or a --preact flag for a build which uses preact/compat .
If your code is compatible, these builds offer an easy way to take a large chunk off the size of the final bundle, and may even provide a performance boost.
$ nwb react build hello.js
✔ Building React app
File size after gzip:
dist\app.e718700a.js 46.5 KB
$ nwb react build hello.js --preact
✔ Installing preact
✔ Cleaning app
✔ Building Preact (React compat) app
File size after gzip:
dist\app.d786be80.js 12.86 KB
Reusing React Modules
The inferno and preact commands are configured to use their respective compatibility layer for any React code which is imported - you'll only pay the cost of including the compatibility layer in your bundle if you import something which uses React.
e.g. if you want a live "time ago" component for your Inferno app, you can pull react-timeago in:
import Inferno from 'inferno'
import TimeAgo from 'react-timeago'
let date = new Date()
export default function App() {
return <div>
This was rendered <TimeAgo date={date}/>
</div>
}
Rendering the Entry Module
This section doesn't apply to the
nwb webcommand, as you're in charge of rendering and using Hot Module Replacement in a vanilla JavaScript app.
Quick development commands can take care of the boilerplate for rendering and handling top-level Hot Module Replacement for React, Preact and Inferno.
Take advantage of this by writing the entry module you provide in the following ways:
Export a Component
You can export a component and it will be rendered:
import React from 'react'
export default class App extends React.Component {
render() {
return <div>
<h1>Idea!</h1>
</div>
}
}
Export an Element or VNode
You can directly export an Element (React terminology) or VNode (Inferno and Preact equivalent) and it will be rendered:
import Inferno from 'inferno'
export default <div>
<h1>Idea!</h1>
</div>
$ nwb inferno run idea.js
✔ Installing inferno, inferno-compat, inferno-clone-vnode, inferno-create-class and inferno-create-element
Starting Webpack compilation...
Compiled successfully in 3595 ms.
The app is running at http://localhost:3000/
Handle Rendering Yourself
If you're handling the initial render() call yourself, you don't need to provide a target DOM node, as nwb hooks into the render() method so it can keep handling top-level Hot Module Replacement for you.
nwb will use lack of exports from an entry module as indication that you're handling rendering yourself:
import React from 'react'
import {render} from 'react-dom'
class App extends React.Component {
render() {
return <h1>Hello world!</h1>
}
}
render(<App/>, document.getElementById('app'))
Rendering Shims and Hot Module Replacement (HMR)
Hot Module Replacement (HMR) is a Webpack feature which enables patching code changes into a running app.
To support rendering when an entry module exports a Component, React Element or Inferno/Preact VNode, a rendering shim is used by default.
When the rendering shim detects that one of these has been exported from the provided entry module, it will handle the initial render and, where possible, will also accept HMR requests and handle re-render.
React Rendering Shim
nwb react run's configuration uses React Refresh Webpack Plugin to enable Fast Refresh for React components.
To disable this, pass a
--no-hmrflag.
For other HMR scenarios, such as an exported React Element, nwb react run's rendering shim will re-render to the same root DOM node.
Opting out of Rendering Shims with --force
If you don't want to use a rendering shim, pass a --force flag the module you provide will be used as the entry point and you'll be responsible for rendering your app to the DOM.
You will also need to manually deal with HMR for Inferno and Preact if you want it, as they don't currently have Fast Refresh equivalents.
Configuration
nwb supports configuration when you need it.
Configuration Arguments
For quick tweaks, you can pass configuration arguments using dotted paths to set any of the documented configuration properties.
e.g. to enable CSS modules for imported .css stylesheets:
$ nwb react run app.js --webpack.rules.css.modules
e.g. to use a Babel plugin which allows you to use class and for attributes in React JSX:
$ nwb react run app.js --babel.plugins=react-html-attrs
Configuration File
To make configuration tweaks permanent, you can put them in a configuration file.
nwb will automatically use an nwb.config.js if present the working directory, or you can specify a custom file with a --config option.