Getting Started

Edit on GitHub

npm

The recommended channel for downloading Cycle.js as a package is through npm. Create a new directory and run this inside that directory:

npm install xstream @cycle/xstream-run @cycle/dom

This installs xstream, Cycle Run using xstream, and Cycle DOM. Packages xstream and Run are the minimum required API to work with Cycle.js. The Run package includes a single function run(), and Cycle DOM is the standard DOM Driver providing a way to interface with the DOM. You can also use Cycle.js with other stream libraries like RxJS. Your options are:

  • npm install xstream @cycle/xstream-run (recommended if you don’t know what to choose)
  • npm install rx @cycle/rx-run (for RxJS v4)
  • npm install rxjs @cycle/rxjs-run (for RxJS v5+)
  • npm install most @cycle/most-run (for cujo.js most.js)

Packages of the type @org/package are npm scoped packages, supported if your npm installation is version 2.11 or higher. Check your npm version with npm --version and upgrade in order to install Cycle.js.

In case you are not dealing with a DOM-interfacing web application, you can omit @cycle/dom when installing.

First steps

We recommend the use of a bundling tool such as browserify or webpack, in combination with ES6 (a.k.a. ES2015) through a transpiler (e.g. Babel or TypeScript). Most of the code examples in this documentation assume some basic familiarity with ES6. Once your build system is set up, write your main JavaScript source file like:

import xs from 'xstream';
import {run} from '@cycle/xstream-run';
import {makeDOMDriver} from '@cycle/dom';

// ...

The second line imports the function run(main, drivers), where main is the entry point for our whole application, and drivers is a record of driver functions labeled by some name.

Create the main function and the drivers record:

import xs from 'xstream';
import {run} from '@cycle/xstream-run';
import {makeDOMDriver} from '@cycle/dom';

function main() {
  // ...
}

const drivers = {
  DOM: makeDOMDriver('#app')
};

run(main, drivers);

makeDOMDriver(container) from Cycle DOM returns a driver function to interact with the DOM. This function is registered under the key DOM in the drivers object above.

Send messages from main to the DOM driver:

import xs from 'xstream';
import {run} from '@cycle/xstream-run';
import {makeDOMDriver, h1} from '@cycle/dom';

function main() {
  const sinks = {
    DOM: xs.periodic(1000).map(i =>
      h1('' + i + ' seconds elapsed')
    )
  };
  return sinks;
}

const drivers = {
  DOM: makeDOMDriver('#app')
};

run(main, drivers);

We have filled the main() function with some code: returns an object sinks which has an xstream stream defined under the name DOM. This indicates main() is sending the stream as messages to the DOM driver. Sinks are outgoing messages. The stream emits Virtual DOM <h1> elements displaying ${i} seconds elapsed changing over time every second, where ${i} is replaced by 0, 1, 2, etc.

Catch messages from DOM into main and vice-versa:

import xs from 'xstream';
import {run} from '@cycle/xstream-run';
import {makeDOMDriver, div, input, p} from '@cycle/dom';

function main(sources) {
  const sinks = {
    DOM: sources.DOM.select('input').events('click')
      .map(ev => ev.target.checked)
      .startWith(false)
      .map(toggled =>
        div([
          input({attrs: {type: 'checkbox'}}), 'Toggle me',
          p(toggled ? 'ON' : 'off')
        ])
      )
  };
  return sinks;
}

const drivers = {
  DOM: makeDOMDriver('#app')
};

run(main, drivers);

Function main() now takes sources as input. Just like the output sinks, the input sources follow the same structure: an object containing DOM as a property. sources.DOM is an object with a queryable API to get streams. Use sources.DOM.select(selector).events(eventType) to get a stream of eventType DOM events happening on the element(s) specified by selector. This main() function takes the stream of clicks happening on input elements, and maps those toggling events to Virtual DOM elements displaying a togglable checkbox.

We used the div(), input(), p() helper functions to create virtual DOM elements for the respective <div>, <input>, <p> DOM elements, but you can also use JSX with Babel. The following only works if you are building with Babel: (1) install the npm packages babel-plugin-transform-react-jsx and snabbdom-jsx; (2) specify a pragma for JSX as shown in the following example .babelrc file:

{
  "presets": [
    "es2015"
  ],
  "plugins": [
    "syntax-jsx",
    ["transform-react-jsx", {"pragma": "html"}]
  ]
}

(3) import Snabbdom JSX as import {html} from 'snabbdom-jsx';, and then you can utilize JSX:

import xs from 'xstream';
import {run} from '@cycle/xstream-run';
import {makeDOMDriver} from '@cycle/dom';
import {html} from 'snabbdom-jsx';

function main(sources) {
  const sinks = {
    DOM: sources.DOM.select('input').events('click')
      .map(ev => ev.target.checked)
      .startWith(false)
      .map(toggled =>
        <div>
          <input type="checkbox" /> Toggle me
          <p>{toggled ? 'ON' : 'off'}</p>
        </div>
      )
  };
  return sinks;
}

const drivers = {
  DOM: makeDOMDriver('#app')
};

run(main, drivers);

This example portrays the most common problem-solving pattern in Cycle.js: formulate the computer’s behavior as a function of streams: continuously listen to source messages from drivers and continuously provide sinks messages (in our case, Virtual DOM elements) to the drivers. Read the next chapter to get familiar with this pattern.

Quick Start

In the future, you can quickly set up a development and production ready Cycle.js project using the cyc boilerplate.

It comes with babel transpilation, hot-reloading, and an isomorphic server.

Cycle.js as a script

In the rare occasion you need Cycle.js scripts as standalone JavaScript files, you can download them from unpkg: