Custom Provider
custom_provider demonstrates another kind of application architecture customization: instead of putting state into globals, it uses ContextProvider to inject configuration into a subtree. In the recording, the outer workspace switches theme and density, while the nested provider on the right locally overrides the workspace without affecting sibling components.

This recording is generated by docs/tapes/custom-provider.tape and runs examples/advanced/custom_provider.rs.
Provider is scope
Section titled “Provider is scope”The root component builds the current workspace configuration as an ordinary Rust value, then passes it to ContextProvider. It affects only its children:
These values are usually themes, permissions, current workspace, route context, service handles, or page-level configuration. They do not always belong in Atom because they naturally have a boundary: only a particular subtree should see them.
Context has three sources:
| Constructor | Meaning | Fits |
|---|---|---|
Context::owned(value) | Provider owns the value | Page-level configuration, current workspace, route context |
Context::from_ref(&value) | Provider offers an immutable borrow | Read-only configuration or service references already owned outside |
Context::from_mut(&mut value) | Provider offers a mutable borrow | Framework internals temporarily passing route tables or subtree state to the next layer |
Application code should prefer Context::owned. Borrowed contexts live only for the current update subtree and fit low-level components or framework-internal composition. If you want to save a value from from_ref / from_mut for a long time, it should usually become owned configuration, use_state, Atom, or explicit props.
Child components read the nearest Context
Section titled “Child components read the nearest Context”Children use use_context::<T>() to read the type injected by a Provider. Keep the borrow guard in a small block and copy out only the fields needed for rendering:
This avoids accidentally holding an old context borrow when you later call other hooks, render children, or enter a new Provider. The habit is especially important with use_context_mut.
Local overrides
Section titled “Local overrides”Providers can nest. When an inner Provider injects the same type, child components read the nearest layer:
In the recording, ScopedAuditPanel sees nested audit, while the sibling ContextProbe still sees the outer workspace. That is the core value of Provider: scope is clear and overrides do not leak.
try_use_context fits optional capability
Section titled “try_use_context fits optional capability”If a component can work both inside and outside a Provider, use try_use_context:
The try_ variant returns None when there is no Provider or the context is currently borrowed. The asserting use_context / use_context_mut variants panic with clearer diagnostics, which fits components where missing Provider means a programming error.
Boundary with Atom
Section titled “Boundary with Atom”Provider and Atom solve different problems:
| Scenario | Prefer |
|---|---|
| Subtree-level theme, current workspace, page context, local service object | ContextProvider |
| Cross-page, cross-subtree, process-wide shared state | Atom |
| Component-private state that should be released on unmount | use_state |
Do not put page-level state into Atom by default just because “props travel far”. First ask whether it has a natural scope; if it does, Provider gives clearer lifetime and override behavior.
When to write a custom Provider
Section titled “When to write a custom Provider”When multiple components need the same environmental data, but that data should not be a global singleton, wrap your own Provider component. RouterProvider in the routing system follows the same pattern: it combines history, route context, and outlet subtree so page components can read current route abilities only through hooks.
Next, see Custom Hook to learn how business logic can become a use_*, or return to Atom Global State to compare process-wide state boundaries.