Installation
---
An Elysia plugin for per-user session state management. Store and retrieve data tied to individual users across requests with automatic session handling.
View on GitHubPer-User Isolation
Each user gets their own state slice. Button clicks and interactions only affect that user's data.
Automatic Sessions
A secure session cookie is automatically created on first visit. No manual session management required.
HTMX Perfect
Ideal for HTMX apps where server endpoints need to maintain user-specific state across partial page updates.
Type Safe
Full TypeScript support with typed state access through the scopedStore context property.
bun add @absolutejs/scoped-stateInitialize the plugin with your state schema. Each key defines a piece of state with an initial value:
import Elysia from 'elysia';
import { scopedState } from '@absolutejs/scoped-state';
const app = new Elysia()
.use(
scopedState({
count: { value: 0 },
username: { value: '', preserve: true }
})
)
.listen(3000);Access your scoped state through the scopedStore context property. Each user sees and modifies only their own state:
// Access and modify scoped state in route handlers
new Elysia()
.use(scopedState({ count: { value: 0 } }))
.get('/api/count', ({ scopedStore }) => {
// Each user sees their own count
return scopedStore.count;
})
.post('/api/increment', ({ scopedStore }) => {
// Increment only affects this user's state
return ++scopedStore.count;
})
.post('/api/decrement', ({ scopedStore }) => {
return --scopedStore.count;
});Mark state as preserve: true to keep it across page refreshes and navigation. Without this, state resets when users refresh the page or navigate away. Useful for user preferences and session data that should persist:
// The preserve option keeps state across page refreshes and navigation
scopedState({
// This resets when the user refreshes or navigates away
count: { value: 0 },
// This persists across page refreshes and switches
theme: { value: 'light', preserve: true },
username: { value: '', preserve: true }
})Use resetScopedStore() to programmatically reset the user's state to initial values. This respects preserve flags by default. Pass true to ignore preserve flags and reset everything:
// Programmatically reset the user's scoped store
new Elysia()
.use(scopedState({
count: { value: 0 },
theme: { value: 'light', preserve: true }
}))
.post('/api/reset', ({ resetScopedStore }) => {
// Resets count to 0, but keeps theme (respects preserve)
resetScopedStore();
return 'State reset!';
})
.post('/api/full-reset', ({ resetScopedStore }) => {
// Ignores preserve flags, resets everything including theme
resetScopedStore(true);
return 'Full state reset!';
});Scoped state shines with HTMX. Each user's button clicks and interactions only affect their own count, cart, or other state:
// Complete HTMX counter example with scoped state
import Elysia from 'elysia';
import { scopedState } from '@absolutejs/scoped-state';
import { handleHTMXPageRequest } from '@absolutejs/absolute';
new Elysia()
.use(
scopedState({
count: { value: 0 }
})
)
.get('/app', () => handleHTMXPageRequest('./build/pages/counter.html'))
.get('/api/count', ({ scopedStore }) => {
return `<span id="count">${scopedStore.count}</span>`;
})
.post('/api/increment', ({ scopedStore }) => {
return `<span id="count">${++scopedStore.count}</span>`;
})
.post('/api/decrement', ({ scopedStore }) => {
return `<span id="count">${--scopedStore.count}</span>`;
})
.listen(3000);The HTML uses HTMX attributes to call these endpoints:
<!-- counter.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Counter App</title>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
<h1>Personal Counter</h1>
<div>
Count: <span id="count" hx-get="/api/count" hx-trigger="load">0</span>
</div>
<button hx-post="/api/increment" hx-target="#count" hx-swap="outerHTML">
+1
</button>
<button hx-post="/api/decrement" hx-target="#count" hx-swap="outerHTML">
-1
</button>
</body>
</html>The plugin uses a secure session cookie to identify users and maintain their state server-side:
user_session_id cookie is createdUser A
Visits /api/count → sees 0
Calls /api/increment → sees 1
Calls /api/increment → sees 2
User B
Visits /api/count → sees 0 (own state)
Calls /api/increment → sees 1
Independent from User A
user_session_id cookie is created on first requestCurrent package surface
Import surface · click to copy
const scopedState: <Setup extends Record<string, ScopedStateConfig<unknown>>>(setup: Setup) => Elysia<"", {
decorator: {};
store: {
scoped: ScopedRecord<{ [K in keyof ValueOnly<Setup>]: ValueOnly<Setup>[K]; }>;
};
derive: {
readonly scopedStore: { [K in keyof ValueOnly<Setup>]: ValueOnly<Setup>[K]; };
readonly resetScopedStore: (ignorePreserve?: boolean) => void;
};
resolve: {};
}, {
typebox: {};
error: {};
}, {
schema: import("elysia").MergeSchema<import("elysia").MergeSchema<{}, {}, "">, {}, "">;
standaloneSchema: {};
macro: {};
macroFn: {};
parser: {};
}, {}, {
derive: {};
resolve: {};
schema: {};
standaloneSchema: {};
}, {
derive: {};
resolve: {};
schema: {};
standaloneSchema: {};
}>;@absolutejs/scoped-stateOutcomes
---
Per-user scoped store
Function / Property — Description
Hardening checklist
Follow in order