## Table of Contents
- [Features](#features)
- [Example](#example)
- [Philosophy](#philosophy)
- [Events](#events)
- [State](#state)
- [Routing](#routing)
- [Server Rendering](#server-rendering)
- [Components](#components)
- [Optimizations](#optimizations)
- [FAQ](#faq)
- [API](#api)
- [Installation](#installation)
- [See Also](#see-also)
- [Support](#support)
## Features
- __minimal size:__ weighing `4kb`, Choo is a tiny little framework
- __event based:__ our performant event system makes writing apps easy
- __small api:__ with only 6 methods there's not much to learn
- __minimal tooling:__ built for the cutting edge `browserify` compiler
- __isomorphic:__ renders seamlessly in both Node and browsers
- __very cute:__ choo choo!
## Example
```js
var html = require('choo/html')
var devtools = require('choo-devtools')
var choo = require('choo')
var app = choo()
app.use(devtools())
app.use(countStore)
app.route('/', mainView)
app.mount('body')
function mainView (state, emit) {
return html`
count is ${state.count}
`
function onclick () {
emit('increment', 1)
}
}
function countStore (state, emitter) {
state.count = 0
emitter.on('increment', function (count) {
state.count += count
emitter.emit('render')
})
}
```
Want to see more examples? Check out the [Choo handbook][handbook].
## Philosophy
We believe programming should be fun and light, not stern and stressful. It's
cool to be cute; using serious words without explaining them doesn't make for
better results - if anything it scares people off. We don't want to be scary,
we want to be nice and fun, and then _casually_ be the best choice around.
_Real casually._
We believe frameworks should be disposable, and components recyclable. We don't
want a web where walled gardens jealously compete with one another. By making
the DOM the lowest common denominator, switching from one framework to another
becomes frictionless. Choo is modest in its design; we don't believe it will
be top of the class forever, so we've made it as easy to toss out as it is to
pick up.
We don't believe that bigger is better. Big APIs, large complexities, long
files - we see them as omens of impending userland complexity. We want everyone
on a team, no matter the size, to fully understand how an application is laid
out. And once an application is built, we want it to be small, performant and
easy to reason about. All of which makes for easy to debug code, better results
and super smiley faces.
## Events
At the core of Choo is an event emitter, which is used for both application
logic but also to interface with the framework itself. The package we use for
this is [nanobus](https://github.com/choojs/nanobus).
You can access the emitter through `app.use(state, emitter, app)`, `app.route(route,
view(state, emit))` or `app.emitter`. Routes only have access to the
`emitter.emit` method to encourage people to separate business logic from
render logic.
The purpose of the emitter is two-fold: it allows wiring up application code
together, and splitting it off nicely - but it also allows communicating with
the Choo framework itself. All events can be read as constants from
`state.events`. Choo ships with the following events built in:
### `'DOMContentLoaded'`|`state.events.DOMCONTENTLOADED`
Choo emits this when the DOM is ready. Similar to the DOM's
`'DOMContentLoaded'` event, except it will be emitted even if the listener is
added _after_ the DOM became ready. Uses
[document-ready](https://github.com/bendrucker/document-ready) under the hood.
### `'render'`|`state.events.RENDER`
This event should be emitted to re-render the DOM. A common pattern is to
update the `state` object, and then emit the `'render'` event straight after.
Note that `'render'` will only have an effect once the `DOMContentLoaded` event
has been fired.
### `'navigate'`|`state.events.NAVIGATE`
Choo emits this event whenever routes change. This is triggered by either
`'pushState'`, `'replaceState'` or `'popState'`.
### `'pushState'`|`state.events.PUSHSTATE`
This event should be emitted to navigate to a new route. The new route is added
to the browser's history stack, and will emit `'navigate'` and `'render'`.
Similar to
[history.pushState](http://devdocs.io/dom/history_api).
### `'replaceState'`|`state.events.REPLACESTATE`
This event should be emitted to navigate to a new route. The new route replaces
the current entry in the browser's history stack, and will emit `'navigate'`
and `'render'`. Similar to
[history.replaceState](http://devdocs.io/dom/history#history-replacestate).
### `'popState'`|`state.events.POPSTATE`
This event is emitted when the user hits the 'back' button in their browser.
The new route will be a previous entry in the browser's history stack, and
immediately afterward the`'navigate'` and `'render'`events will be emitted.
Similar to [history.popState](http://devdocs.io/dom_events/popstate). (Note
that `emit('popState')` will _not_ cause a popState action - use
`history.go(-1)` for that - this is different from the behaviour of `pushState`
and `replaceState`!)
### `'DOMTitleChange'`|`state.events.DOMTITLECHANGE`
This event should be emitted whenever the `document.title` needs to be updated.
It will set both `document.title` and `state.title`. This value can be used
when server rendering to accurately include a `` tag in the header.
This is derived from the
[DOMTitleChanged event](https://developer.mozilla.org/en-US/docs/Web/Events/DOMTitleChanged).
## State
Choo comes with a shared state object. This object can be mutated freely, and
is passed into the view functions whenever `'render'` is emitted. The state
object comes with a few properties set.
When initializing the application, `window.initialState` is used to provision
the initial state. This is especially useful when combined with server
rendering. See [server rendering](#server-rendering) for more details.
### `state.events`
A mapping of Choo's built in events. It's recommended to extend this object
with your application's events. By defining your event names once and setting
them on `state.events`, it reduces the chance of typos, generally autocompletes
better, makes refactoring easier and compresses better.
### `state.params`
The current params taken from the route. E.g. `/foo/:bar` becomes available as
`state.params.bar` If a wildcard route is used (`/foo/*`) it's available as
`state.params.wildcard`.
### `state.query`
An object containing the current queryString. `/foo?bin=baz` becomes `{ bin:
'baz' }`.
### `state.href`
An object containing the current href. `/foo?bin=baz` becomes `/foo`.
### `state.route`
The current name of the route used in the router (e.g. `/foo/:bar`).
### `state.title`
The current page title. Can be set using the `DOMTitleChange` event.
### `state.components`
An object _recommended_ to use for local component state.
### `state.cache(Component, id, [...args])`
Generic class cache. Will lookup Component instance by id and create one if not
found. Useful for working with stateful [components](#components).
## Routing
Choo is an application level framework. This means that it takes care of
everything related to routing and pathnames for you.
### Params
Params can be registered by prepending the route name with `:routename`, e.g.
`/foo/:bar/:baz`. The value of the param will be saved on `state.params` (e.g.
`state.params.bar`). Wildcard routes can be registered with `*`, e.g. `/foo/*`.
The value of the wildcard will be saved under `state.params.wildcard`.
### Default routes
Sometimes a route doesn't match, and you want to display a page to handle it.
You can do this by declaring `app.route('*', handler)` to handle all routes
that didn't match anything else.
### Querystrings
Querystrings (e.g. `?foo=bar`) are ignored when matching routes. An object
containing the key-value mappings exists as `state.query`.
### Hash routing
By default, hashes are ignored when routing. When enabling hash routing
(`choo({ hash: true })`) hashes will be treated as part of the url, converting
`/foo#bar` to `/foo/bar`. This is useful if the application is not mounted at
the website root. Unless hash routing is enabled, if a hash is found we check if
there's an anchor on the same page, and will scroll the element into view. Using
both hashes in URLs and anchor links on the page is generally not recommended.
### Following links
By default all clicks on `` tags are handled by the router through the
[nanohref](https://github.com/choojs/nanohref) module. This can be
disabled application-wide by passing `{ href: false }` to the application
constructor. The event is not handled under the following conditions:
- the click event had `.preventDefault()` called on it
- the link has a `target="_blank"` attribute with `rel="noopener noreferrer"`
- a modifier key is enabled (e.g. `ctrl`, `alt`, `shift` or `meta`)
- the link's href starts with protocol handler such as `mailto:` or `dat:`
- the link points to a different host
- the link has a `download` attribute
:warn: Note that we only handle `target=_blank` if they also have
`rel="noopener noreferrer"` on them. This is needed to [properly sandbox web
pages](https://mathiasbynens.github.io/rel-noopener/).
### Navigating programmatically
To navigate routes you can emit `'pushState'`, `'popState'` or
`'replaceState'`. See [#events](#events) for more details about these events.
## Server Rendering
Choo was built with Node in mind. To render on the server call
`.toString(route, [state])` on your `choo` instance.
```js
var html = require('choo/html')
var choo = require('choo')
var app = choo()
app.route('/', function (state, emit) {
return html`