jus is a development server and build tool for making static websites with no configuration and no boilerplate code. It has built-in support for browserify, ES6 and ES2015 with Babel, React JSX, GitHub Flavored markdown, syntax highlighting, Sass, Less, Stylus, Myth, Handlebars, browsersync and more.

The year is 2017 and you're building a new website. At first you just create a single HTML file with some inline scripts and style tags. This works for a bit, but soon your code grows and you decide to extract the styles and scripts into standalone files. This is slightly better, but eventually you want to do something more sophisticated, like writing your stylesheets in Sass, or concatenating and minifying assets, or using npm dependencies with browserify. These conveniences are essential to building a website of any magnitude, but setting them up is tedious and time-consuming. It's at this point in the project that your attention turns from the creative to the mundane. Rather than building, you're now configuring.

In this day and age, most developers would turn to Gulp, npm scripts, Jekyll or one of dozens of static site tools. This is where jus comes in as an alternative.

There is no setup with jus. It has just two commands: serve and build. Run jus serve in your project directory and you've got a live develpment server running, watching for file changes, autorefreshing your browser with browsersync, and serving your content with clean URLs. Write a foo.sass file and it'll be served at /foo.css. Use an npm-style require statement in your script, and jus will serve it up as a browserified bundle. Write React's JSX syntax and it'll be transpiled to javascript on the fly. Write a GitHub-flavored /markdown/file.md and it'll be served as syntax-highlighted HTML at /markdown/file.

When it's time to deploy, run jus build to compile your site down into plain old HTML, CSS, and Javascript files, ready for deployment to GitHub Pages, Surge, or any other static site host that supports clean URLs.

jus requires node 4 or greater, because it uses some newer Javascript features. Install the command-line interface globally, then run it to see usage instructions:

npm i -g jus && jus

jus has a lot of dependencies, so it takes a while to install. Maybe go grab a ☕️ and read up on how to make npm faster.

If you like to learn by example, check out the repos of sites using jus. Otherwise, read on...

Pages are written in Markdown, HTML, Handlebars, or any combination thereof. At render time each page is passed a Handlebars context object containing metadata about all the files in the directory.

Extensions: html|hbs|handlebars|markdown|md

All javascript files* in your project are automatically browserified and babelified using the es2015 and react presets.

You can use node-style require statements to include node and npm modules in your code:

const url = require('url').parse('https://example.com')
console.log(`the domain is ${url.host}`)

You can also use ES6-style imports, if you prefer:

import React from 'react'
import ReactDOM from 'react-dom'
import domready from 'domready'
domready(() => {
  // do some React magic 

Scripts are browserified using babel-preset-react, so you can write JSX in your scripts.

*Scripts ending in .min.js are assumed to be pre-processed artifacts and are not browserified.

Extensions: js|jsx|es|es6

Stylesheets can be written in Sass, SCSS, Less, Stylus, Myth, or plain old CSS. Use whatever preprocessor suits your fancy.

Extensions: css|less|sass|scss|styl

When the jus server is initialized, it recursively finds all the files in the directory tree, ignoring node_modules, .git, and other unwanted patterns. These files are then stored in memory in an array called files. For convenience, this list of files is broken down into smaller arrays by type: an array for pages, another array for scripts, etc.

  files: [...],
  pages: [...]
  scripts: [...]
  stylesheets: [...]
  images: [...]
  datafiles: [...]

When you request a page, the server renders the page on the fly, passing this object to the given page's template. This means every page has access to metadata about every file in the site at render time.

Using handlebars in your pages is entirely optional. If your pages don't need to do any dynamic rendering at build time, that's okay. The context will simply be ignored at render time.

jus supports HTML frontmatter. This allows you to add key-value metadata to your pages:

title: Alice in Wonderland
year: 1951

Any such values present in an HTML comment at the top of a page are made available in that page's Handlebars context object at render time.

Note: Jekyll uses YAML for frontmatter, but jus uses HTML, because it can be included in a file without adversely affecting the way it renders on github.com.

Handlebars templates can be used to wrap layouts around your pages.

Extensions: html|hbs|handlebars|markdown|md|mdown

jus provides a number of helper functions you can use in your handlebars templates. All of the helpers are from lobars, a collection of utility functions plucked directly from lodash.

lobars includes comparison helpers like endsWith, eq, gt, gte, includes, isArray, isBoolean, isDate, isEmpty, isMatch, isNumber, isString, isSymbol, isUndefined, lt, lte, startsWith and more.

Here's an example use of the gte (greater than or equal to) helper:

{{#gte age 21}}
  You are old enough to drink in the United States.

lobars also provides helpers for manipulating input like camelCase, capitalize, escape, kebabCase, lowerCase, lowerFirst, pad, padEnd, padStart, parseInt, repeat, replace, snakeCase, split, startCase, template, toLower, toUpper, trim, trimEnd, trimStart, truncate, unescape, upperCase, upperFirst, and more.

Here's how you use the string helpers:

{{lowerCase someString}}

Delicious metadata is extracted from images and included in the Handlebars context object, which is accessible to every page.

Extensions: png|jpg|gif|svg

JSON and YML files are slurped into the Handlebars context object, which is accessible to every page.

Extensions: json|yaml|yml

jus uses a clean URL strategy that is compatible with GitHub Pages and surge.sh. In essence, pages get their extension lopped off, and pages named index inherit the name of their directory.

Filename URL
index.html /
nested/index.html /nested
nested/page.html /nested/page
also/markdown.md /also/markdown
also/handlebars.hbs /also/handlebars
stylesheet.scss /stylesheet.css
stylesheet.sass /stylesheet.css
stylesheet.styl /stylesheet.css
stylesheet.styl /stylesheet.css

Add the following to your package.json:

  "homepage": "http://example.com",
  "scripts": {
    "start": "jus serve",
    "deploy": "npm run build && npm run commit && npm run push && npm run open",
    "build": "jus build . dist",
    "commit": "git add dist && git commit -m 'update dist'",
    "push": "git subtree push --prefix dist origin gh-pages",
    "open": "open $npm_package_homepage"

Now whenever you want to publish to GitHub Pages, run:

npm run deploy

Note: GitHub's User Pages (like yourname.github.io) are built from the master branch, whereas Project Pages (like yourname.github.io/project) are built from the gh-pages branch. Be aware of this when setting up your npm scripts.

Note: GitHub's CDN can take a minute to update, so you might have to refresh a few times when visiting.

surge.sh is an awesome new platform for publishing static websites.

Install the Surge CLI:

 npm i -g surge

Add the following to your package.json:

  "homepage": "http://example.com",
  "scripts": {
    "start": "jus serve",
    "deploy": "npm run build && npm run build && npm run open",
    "build": "jus build . dist",
    "push": "surge dist $npm_package_homepage",
    "open": "open $npm_package_homepage"

Now whenever you want to publish to Surge, run:

npm run deploy

jus was inspired by a number of existing tools:

Sometimes real examples are the easiest way to learn. Check out these open-source sites built with jus:

Check out the jus source code on GitHub at jus/jus.

Build Status