@sync0/react
v0.1.2
Published
React adapter for @sync0/core — useField() hook + convenience hooks
Maintainers
Readme
@sync0/react
React adapter for @sync0/core.
Two complementary APIs: useField() for subscribing to any field (including ones shared across components), and a set of convenience hooks (useUrlField, useStorageField, …) for component-local fields that manage their own lifecycle.
Installation
npm install @sync0/react @sync0/coreRequires React ≥ 18.
Quick start
Convenience hooks — local field per component
import {
useUrlField,
useStorageField,
useSessionField,
useCookieField,
useServerField,
} from '@sync0/react';
function FilterPanel() {
const [query, setQuery] = useUrlField('', 'q');
const [theme, setTheme] = useStorageField('light', 'theme');
const [draft, setDraft] = useSessionField('', 'draft');
const [locale, setLocale] = useCookieField('en', 'locale', { maxAge: 31_536_000 });
// Server field — third element is conflict state
const [notes, setNotes, { conflict, dismissConflict }] = useServerField('', 'notes');
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
{conflict && (
<div className="banner">
Updated elsewhere — <button onClick={dismissConflict}>Dismiss</button>
</div>
)}
<textarea value={notes} onChange={e => setNotes(e.target.value)} />
</>
);
}Each hook creates the field once on mount (via useRef) and cleans up engine listeners on unmount automatically.
useField() — subscribe to a shared field
import { useField, useOnConflict, urlField } from '@sync0/react';
// Defined once outside the component — shared across all instances
const searchField = urlField('', 'q');
function SearchBar() {
const [query, setQuery] = useField(searchField);
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
function ResultCount() {
const [query] = useField(searchField); // same field, separate subscription
return <p>{query.length} chars</p>;
}useField uses useSyncExternalStore — concurrent-mode safe and SSR-ready.
All hooks
| Hook | Returns | Backed by |
|---|---|---|
| useField(f) | [value, set] | any Field<T> |
| useLocalField(initial) | [value, set] | in-memory |
| useUrlField(initial, key) | [value, set] | URL search param |
| useStorageField(initial, key) | [value, set] | localStorage |
| useSessionField(initial, key) | [value, set] | sessionStorage |
| useCookieField(initial, key, opts?) | [value, set] | document.cookie |
| useServerField(initial, table, opts?) | [value, set, { conflict, dismissConflict }] | server adapter |
| useOnConflict(f) | { conflict, dismissConflict } | any server Field<T> |
Server adapter setup
Call configureServerAdapter() once before rendering any server-field components:
import { configureServerAdapter } from '@sync0/react';
configureServerAdapter(new SupabaseAdapter(supabase)); // or your own adapterCustom codec (URL / storage / cookie fields)
const page = useUrlField(1, 'page', {
codec: { encode: String, decode: Number },
});Links
- Full documentation & examples
@sync0/core— framework-agnostic engine@sync0/angular— Angular Signal adapter@sync0/vue— Vue 3 Ref adapter
