Nuestate: URL-first state management

Nuestate puts your application state in the URL by default. This makes bookmarking, sharing, and browser navigation work naturally without extra code. State changes automatically update the URL and trigger component re-renders.

The library provides a simple state proxy object for reading and writing application state directly. Changes are automatically persisted to the URL, browser storage, or kept in memory based on your configuration.

Why URL-first?

Most state management solutions treat the URL as an afterthought. You have to manually sync state with the URL, handle browser navigation, and write extra code for bookmarking and sharing.

Nuestate flips this around. Your state lives in the URL by default, so these features work automatically:

Bookmarking works - Users can bookmark any application state and return to it later

Sharing works - Send someone a URL and they see exactly what you see

Browser navigation works - Back/forward buttons navigate through state changes

Standard routing works - Regular <a href> tags become SPA navigation with autolink

No sync code - No need to manually keep URL and state in sync

How it works

Import and use the state object anywhere in your application:

import { state } from 'state'

// Read and write state
state.view = 'users'     // URL updates to include view=users
state.search = 'john'    // URL becomes ?view=users&search=john

Configure where different pieces of state should live:

state.setup({
  route: '/app/:section/:id',
  query: ['search', 'filter', 'page'],
  session: ['user', 'preferences'],
  local: ['theme', 'language']
})

// Route parameters update the URL path
state.section = 'products'
state.id = '123'
// URL becomes: /app/products/123

// Query parameters update the URL search
state.search = 'shoes'
// URL becomes: /app/products/123?search=shoes

Listen to state changes:

state.on('search filter', async (changes) => {
  const results = await fetchResults(changes.search, changes.filter)
  state.results = results
})

Use state directly in components with standard DOM events:

<input value="{ state.search }" :oninput="state.search = $event.target.value">

See the State API documentation for complete details on all methods and configuration options.

Standard routing

Nuestate turns regular HTML links into SPA navigation with the autolink option:

state.setup({
  route: '/app/:section/:id',
  autolink: true
})

Now standard <a href> tags automatically update state instead of reloading the page:

<!-- These work as SPA navigation -->
<a href="/app/users">Users</a>
<a href="/app/users/123">User Details</a>
<a href="/app/products">Products</a>

<!-- External links still work normally -->
<a href="https://example.com">External Site</a>

When someone clicks /app/users/123, Nuestate automatically sets:

  • state.section = 'users'

  • state.id = '123'

No routing libraries, no special components. Just HTML links that work exactly as you'd expect, but faster.

Storage types

Different storage types serve different purposes:

URL parameters - For shareable, bookmarkable state that defines what the user sees

Session storage - For user-specific data that should persist during the browser session

Local storage - For user preferences that should persist across sessions

Memory - For temporary data and UI state that doesn't need persistence

Choose the right storage type based on how long the data should live and whether it should be shareable.

Less is More

Nuestate keeps both your API surface and your applications small:

Small API - Just read and write to the state object. No stores, reducers, actions, or complex patterns to learn.

Less boilerplate - No manual URL synchronization code. No setup for browser navigation. No extra logic for bookmarking and sharing.

Smaller apps - Under 2KB with zero dependencies. Your total bundle stays small when state management doesn't bloat it.

Less to learn - State works like any JavaScript object. If you understand obj.prop = value, you understand Nuestate.

The library does one thing well: manage application state with URL synchronization built in. Like a UNIX command.

Installation

bun install nuestate

Or use it directly in the browser:

<script type="module">
  import { state } from '//esm.sh/nuestate'
</script>