Hooks
Composable, function-based primitives for state, events, data fetching, i18n, and more.
The hooks module provides a familiar pattern inspired by React hooks but designed for
vanilla JavaScript. Access everything through Aura.hooks.
Quick Start
The hooks module is available on every Aura instance. No additional imports are needed.
// Reactive state const [count, setCount] = Aura.hooks.useState('count', 0); setCount(count() + 1); // Event subscription with auto-cleanup const unsub = Aura.hooks.useEvent('user:login', (user) => { console.log('Welcome', user.name); }); // Fetch data with loading state const { data, loading, error, refetch } = Aura.hooks.useFetch('/api/users'); // Persist to localStorage const [theme, setTheme] = Aura.hooks.useStorage('theme', 'dark');
register()
Register a custom hook by name so it can later be invoked with use(). This is the foundation for creating reusable, shareable hook logic.
| Param | Type | Description |
|---|---|---|
| name | string | Unique name for the hook. |
| fn | (...args) => unknown | The hook function to register. |
Aura.hooks.register('useToggle', (key, initial = false) => { const [val, setVal] = Aura.hooks.useState(key, initial); const toggle = () => setVal(!val()); return [val, toggle]; });
use()
Invoke a previously registered custom hook. Throws if the hook has not been registered.
| Param | Type | Description |
|---|---|---|
| name | string | Name of the registered hook. |
| ...args | unknown[] | Arguments forwarded to the hook function. |
Returns: The return value of the hook function, cast to T.
const [isOpen, toggleOpen] = Aura.hooks.use('useToggle', 'modal', false); console.log(isOpen()); // false toggleOpen(); console.log(isOpen()); // true
useState()
Creates a reactive state binding. Returns a [getter, setter] tuple backed by the Aura state store. If the key does not exist yet, it is initialized with the provided value.
| Param | Type | Description |
|---|---|---|
| key | string | State key name. |
| initial | T | Initial value (only set if key is undefined). |
Returns: [getter(), setter(value)] — the getter is a function you call to read the current value.
const [count, setCount] = Aura.hooks.useState('count', 0); console.log(count()); // 0 setCount(5); console.log(count()); // 5 // Multiple components can share the same key const [count2] = Aura.hooks.useState('count'); console.log(count2()); // 5 (same underlying state)
useEvent()
Subscribe to an Aura event. Returns an unsubscribe function. Optionally accepts a priority (lower numbers fire first).
| Param | Type | Description |
|---|---|---|
| event | string | Event name to listen for. |
| handler | (...args) => void | Callback invoked when the event fires. |
| priority | number? | Optional priority. Lower numbers fire first. |
Returns: () => void — call this to unsubscribe.
const unsub = Aura.hooks.useEvent('cart:update', (items) => { renderCart(items); }); // With priority (fires before default handlers) Aura.hooks.useEvent('cart:update', logAnalytics, 1); // Clean up when done unsub();
useComputed()
Derives a computed value from the state store. The function receives the full state object and should return the derived value. Returns a getter function.
| Param | Type | Description |
|---|---|---|
| key | string | Key under which the computed value is stored. |
| fn | (state) => T | Derivation function that receives the full state object. |
Returns: () => T — getter that returns the latest computed value.
const total = Aura.hooks.useComputed('cartTotal', (state) => { return state.items.reduce((sum, i) => sum + i.price, 0); }); console.log(total()); // e.g. 59.97
useFetch()
Declarative data fetching with reactive data, loading, and error getters. By default the request fires immediately; pass auto: false to defer it.
| Param | Type | Description |
|---|---|---|
| url | string | The URL to fetch. |
| opts.method | string? | HTTP method. Defaults to "GET". |
| opts.body | unknown? | Request body (used for non-GET methods). |
| opts.auto | boolean? | Auto-fetch on creation. Defaults to true. |
Returns: An object with reactive getters and a refetch method.
// Auto-fetch (default) const { data, loading, error, refetch } = Aura.hooks.useFetch('/api/users'); // Check state reactively if (loading()) showSpinner(); if (error()) showError(error()); if (data()) renderUsers(data()); // Deferred fetch const post = Aura.hooks.useFetch('/api/users', { method: 'POST', body: { name: 'Ada' }, auto: false }); // Fire manually later await post.refetch();
useI18n()
Returns a translate function that delegates to the Aura i18n module. Supports parameterized strings.
Takes no arguments.
Returns: (key, params?) => string — a translation function.
const t = Aura.hooks.useI18n(); console.log(t('hero.title')); // "Welcome to Aura" console.log(t('greeting', { name: 'Ada' })); // "Hello, Ada!"
useStorage()
Like useState(), but backed by localStorage. The value persists across page reloads. Returns a [getter, setter] tuple.
| Param | Type | Description |
|---|---|---|
| key | string | localStorage key. |
| initial | T? | Default value if the key does not exist in storage. |
Returns: [getter(), setter(value)] — reads/writes localStorage through the Aura storage module.
const [theme, setTheme] = Aura.hooks.useStorage('theme', 'dark'); console.log(theme()); // 'dark' (or last saved value) setTheme('light'); // Survives page reload // Great for user preferences const [lang, setLang] = Aura.hooks.useStorage('locale', 'en');
useWatch()
Subscribe to changes on a specific state key. The handler fires whenever the value changes. Returns an unsubscribe function.
| Param | Type | Description |
|---|---|---|
| key | string | State key to watch. |
| handler | (value) => void | Callback receiving the new value. |
Returns: () => void — call to stop watching.
const unsub = Aura.hooks.useWatch('user', (user) => { document.getElementById('name').textContent = user.name; }); // Later, clean up unsub();
useSetup()
Run a setup function that may call useEvent() and useWatch() internally. All subscriptions created inside the setup are automatically tracked. The returned teardown function cleans them all up at once.
| Param | Type | Description |
|---|---|---|
| setupFn | () => void | Function that sets up hooks. useEvent and useWatch calls inside are auto-tracked. |
Returns: () => void — call to teardown all collected subscriptions.
const teardown = Aura.hooks.useSetup(() => { // All subscriptions are auto-tracked Aura.hooks.useEvent('resize', handleResize); Aura.hooks.useEvent('scroll', handleScroll); Aura.hooks.useWatch('user', updateUI); Aura.hooks.useWatch('theme', applyTheme); }); // One call cleans up all 4 subscriptions teardown();
Custom Hooks
You can build reusable hook abstractions by combining register() and use(). This pattern lets you share stateful logic across your application without coupling it to a component framework.
Creating a custom hook
// Register a reusable "useAuth" hook Aura.hooks.register('useAuth', () => { const [user, setUser] = Aura.hooks.useState('auth.user', null); const [token, setToken] = Aura.hooks.useStorage('auth.token'); const login = async (credentials) => { const { data } = Aura.hooks.useFetch('/api/login', { method: 'POST', body: credentials, auto: false }); setUser(data()); setToken(data().token); }; const logout = () => { setUser(null); setToken(null); }; return { user, token, login, logout }; });
Using a custom hook
const auth = Aura.hooks.use('useAuth'); if (auth.user()) { console.log('Logged in as', auth.user().name); } else { await auth.login({ email: 'ada@example.com', password: '...' }); }
Another example: useDebounce
Aura.hooks.register('useDebounce', (key, delay = 300) => { const [value, setValue] = Aura.hooks.useState(key); const [debounced, setDebounced] = Aura.hooks.useState(key + '.debounced'); let timer; Aura.hooks.useWatch(key, (val) => { clearTimeout(timer); timer = setTimeout(() => setDebounced(val), delay); }); return { value, setValue, debounced }; }); const search = Aura.hooks.use('useDebounce', 'searchQuery', 500);
Comparison with React Hooks
If you are coming from React, here is how Aura hooks map to their React equivalents. The main difference is that Aura getters are functions you call, not raw values.
| React | Aura.js | Notes |
|---|---|---|
| useState(0) | hooks.useState('key', 0) | Aura requires a string key. Getter is a function: count(). |
| useEffect(() => ...) | hooks.useSetup(() => ...) | Collects useEvent/useWatch cleanups automatically. |
| useMemo(() => ...) | hooks.useComputed('key', fn) | Derives from global state, not local deps. |
| useContext(...) | hooks.useState('key') | All Aura state is globally shared — no provider needed. |
| useReducer(...) | hooks.register('useReducer', ...) | Build your own with register() + useState(). |
| useRef() | hooks.useState('ref.key') | Use state with a unique key for mutable refs. |
| Custom Hook | hooks.register() + hooks.use() | Named registry instead of function imports. |
// React const [count, setCount] = useState(0); return <p>{count}</p>; // Aura.js const [count, setCount] = Aura.hooks.useState('count', 0); el.textContent = count(); // Note: getter is a function call