Need help with crio?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

208 Stars 11 Forks MIT License 213 Commits 0 Opened issues


Immutable objects and arrays in a natural way

Services available


Need anything else?

Contributors list

# 143,788
165 commits
# 259,621
2 commits
# 26,902
1 commit


Immutable JS objects with a natural API

Jump to the API

API Documentation

Upgrade notice

If upgrading from

and earlier versions, please check the changelog for breaking changes.


// ES2015
import crio from 'crio';

// CommonJS const crio = require('crio').default;

// UMD const crio = window.crio;


// you can assign with crio() directly
const crioArray = crio(['foo']);
const updatedCrioArray = crioArray.push('bar');

const crioObject = crio({foo: 'bar'}); const updatedCrioObject = crioObject.set('bar', 'baz');

// or use the convenience methods const otherCrioArray = crio.array(['bar']); const updatedOtherCrioArray = otherCrioArray.push('bar');

const otherCrioObject = crio.object({bar: 'baz'}); const updatedOtherCrioObject = otherCrioObject.set('bar', 'baz');

What is immutable?

When something is described as immutable, it means that it cannot change after it has been created. In JavaScript terms, this means that any attempted change to an object results in a brand new object being created, without changing the original object.

Why is this helpful?

The concept of immutability already exists in a lot of places in JavaScript, for example:

const two = 2;
const three = 3;
const five = two + three;

By adding together two and three you expect to get five, however you don't expect the value of two to change. You can continue working with it even after using it in an expression:

const two = 2;
const three = 3;
const five = two + three;
const four = two * two;

This is true of strings, numbers, undefined, and null, and is an expected behavior. The same idea, however, is not true for complex objects in JavaScript. For example:

const foo = ['foo'];
const fooBar = foo.push('bar');

The expectation is that you have pushed the value of "bar" into foo and created a new array bar that contains "foo, bar", however in reality this is what happens:

const foo = ['foo'];
const fooBar = foo.push('bar');

console.log(foo); // ['foo', 'bar'] console.log(fooBar); // 1

Basically, you have mutated foo so that it is no longer empty, and what the .push() method returns is actually the index of the item you just added. This double-standard of expectations creates a lot confusion from a development perspective, but also makes keeping track of the state of your application very difficult because there is no traceability of what transactions have occurred to create that state at any given point. This can create a lot of difficult-to-diagnose bugs and potential future regression points.

Enter crio

crio attempts to solve the problem by closing the "immutable loop" on collection items, meaning it applies immutability to objects that are normally mutable by nature by replacing mutating methods with immutable counterparts. As a point of reference:

Naturally immutable objects

  • Numbers
  • Strings
  • undefined
  • null

Naturally mutable objects

  • Arrays
  • Dates (not covered by crio)
  • Objects

The API is the same as you already know working with those objects, as well as several helpful

-specific functions. You can work with the objects as you normally would with other libraries (
, for example) with the exception that any setting happens via
rather than direct index / property setting. There is also no change to the protoypes of native objects, so you can apply this on your existing code go-forward. Basically, you shouldn't even notice you aren't working with the native objects, save for the fact everything is immutable.

Why not just use X immutable library?

is quite nice, and very highly regarded by the community, however it creates an opaque object that cannot be used with other external libraries (namely

) without converting back to vanilla JS. This lack of interoperability creates a lot of hoops to jump through at certain points of development, and also trains you to not necessarily think in JS, but in the library.

has some great ideas, but they do not try to replace mutable methods with immutable ones, they just throw errors when you attempt them and its up to you to figure out the "right way". Also, it can be quite slow for certain operations.

Bottom line, I support each of these projects because they are trying to instill immutability in JavaScript practices, but I took a different approach that I consider the best of both worlds. :)

Browser support

  • Chrome (all versions)
  • Firefox (all versions)
  • Edge (all versions)
  • Opera 15+
  • IE 11+
  • Safari 8+
  • iOS 8+
  • Android 4+

Node support

  • 4+


Recursive objects are not allowed

Immutable objects with recursive values are basically impossible, and trying them will cause a stack overflow, so be mindful of that!


Standard stuff, clone the repo and

npm install
dependencies. The npm scripts available:
  • benchmark
    => run benchmarks in node
  • benchmark:watch
    => run
    with persistent watcher for changes
  • build
    => run webpack to build crio.js with NODE_ENV=development
  • build:minifed
    => run webpack to build crio.min.js with NODE_ENV=production
  • compile-for-publish
    => run
  • dev
    => run webpack dev server to run example app (playground!)
  • dev:production
    => runs
    but with NODE_ENV=production
  • dist
    => runs
  • lint
    => run ESLint against all files in the
  • prepublish
    => runs
  • test
    => run AVA test functions with
  • test:watch
    => same as
    , but runs persistent watcher
  • transpile
    => run babel against all files in
    to create files in

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.