Bonds
State without prop drilling.
A Bond is a class-based state container that lives in Svelte context. Root components create one; every descendant reads it automatically — no props passed between them.
What are Bonds?
A two-part architecture: BondState holds reactive data, Bond manages elements and context.
A Bond is split into two classes. BondState owns the
reactive props and mutation methods — it's where your component's logic lives. Bond owns the DOM element references (via BondAtom subclasses), generates element spreads with ARIA and event handlers, and puts itself into
Svelte context via .share().
Child components call Bond.get() to
retrieve the bond from context and read state, call methods, or access captured DOM elements
— without any props being threaded between components.
Key features
What Bonds give you that plain stores and prop-drilling don't.
Separation of concerns
BondState owns logic; Bond owns DOM. Neither knows about Svelte component lifecycle.
Automatic element capture
BondAtom attachments track DOM nodes the moment they mount — no manual ref management needed.
Full type safety
Generic typing from props → state → elements → spread. Your IDE knows every method at authoring time.
Fine-grained reactivity
Props are passed as a function — only the properties actually read inside the Bond trigger updates.
Context integration
Built-in .share(), .get(), .set() — share state across any component tree depth.
Customisable via factory
Components accept a factory prop so callers can swap in Bond subclasses for testing or extension.
Creating a Bond
Four pieces: props type, BondState class, BondAtom subclasses, and Bond class.
1 — Define state props and BondState
2 — Create BondAtom subclasses and Bond
Each BondAtom subclass represents one DOM element. Override attrs for ARIA/data attributes and handlers for events. Element capture is automatic — BondAtom.attachments wires up the element reference when the node mounts.
Using Bonds in components
Root components create and share the Bond; children retrieve it from context.
Root component
Call .share() to place the Bond in Svelte context, then spread .spread on each element — it contains attrs, event handlers, and the attachment that captures the DOM node.
Child component
Children call the static .get() to retrieve the Bond. Use bond.element('key') to access captured DOM elements.
Factory prop
Expose a factory prop so callers can inject a custom Bond subclass — useful for testing or extending behavior.
Accessing from a parent
Export a getBond() function from the root component so parent components can imperatively read or mutate bond state.
Architecture deep dive
BondState props, reactive derived values, and context sharing.
BondState — props and logic
this.props calls the function passed to the constructor, returning the live reactive object built by defineState.
Use $derived as a class field for computed values — Svelte 5 Runes work inside class bodies.
Reactive props with defineState
defineProperty creates a reactive getter/setter pair on the props object. When BondState accesses this.props.open,
it runs the getter — and only that getter triggers updates, giving fine-grained reactivity.
Context sharing
.share() wraps setContext.
The static .get() wraps getContext.
Every Bond subclass defines its own unique context key.
When to use Bonds
Bonds are powerful but not always the right tool.
Compound components
Multiple sibling parts (trigger, content, item) that must share state — the primary use case for Bonds.
DOM coordination
When parts need to measure, focus, or position relative to each other — BondAtom element refs make this clean.
Reusable logic
Extract component behavior into a plain class — easy to unit-test without mounting a component.
Extensibility
Subclass a Bond to override ARIA attrs or add behavior without forking the original component.
When not to use Bonds
$state variable is
simpler and clearer. Don't reach for the Bond pattern until you have multiple coordinating parts.Best practices
Guidelines for working effectively with Bonds.
Wrap props with defineState
Pass a function to BondState — not a plain object. defineProperty bridges $bindable props into the Bond without losing reactivity.
Spread .spread on elements
Always use {...bond.root().spread} — it includes attrs, handlers, and the attachment that captures the element reference.
Use unique CONTEXT_KEY values
Prefix with your package or app name: '@my-app/bond/accordion'. When subclassing an existing Bond, keep the parent key to preserve context compatibility.
Export getBond()
Export a getBond() function from root components so parents can access the bond imperatively when needed.
Name your Bond in the constructor
Pass a name string to super(state, 'my-component') — it becomes the data-bond attribute used for debugging and CSS selectors.
Learn more
See Bonds in action or understand the architecture behind them.