+> **This is the v8 branch.** choo v7 is on `master` and stays untouched
+> until 8.0.0 ships. Everything below describes v8: same API, same
+> philosophy, rebuilt for the platform of 2026. See
+> [`docs/v8.md`](docs/v8.md) for status and the full plan.
-
+Choo is a small framework for building frontend applications with plain
+JavaScript and HTML. State lives in stores, changes flow through an event
+emitter, views are tagged template literals, and re-renders morph the real
+DOM. The whole framework โ core, html engine, morphing, hydration, router,
+event bus โ is **7.97 kB min+gzip** (7.15 kB brotli), enforced by CI.
-## 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')
+import choo from '@choojs/core'
+import html from '@choojs/html'
-var app = choo()
-app.use(devtools())
-app.use(countStore)
-app.route('/', mainView)
-app.mount('body')
+const app = choo()
-function mainView (state, emit) {
- return html`
-
-
count is ${state.count}
-
-
- `
-
- function onclick () {
- emit('increment', 1)
- }
-}
-
-function countStore (state, emitter) {
+app.use((state, emitter) => {
state.count = 0
- emitter.on('increment', function (count) {
- state.count += count
+ emitter.on('increment', (n) => {
+ state.count += n
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`