Context lets you pass data through the component tree without
threading it through every level of props. Define a ReactorContext<T>, provide
a value at any level, and any descendant can read it with UseContext.
A context is a static field with a default value:
static class Contexts
{
public static Context<string> ThemeMode = new("light");
public static Context<string> UserName = new("Guest");
public static Context<int> FontScale = new(16);
}The default value is used when a component calls UseContext but no ancestor
has provided a value. Choose a sensible default — it makes components work
standalone during development.
Use .Provide() on any element to supply a value to its subtree. Use
UseContext() in any descendant to read it:
class ProvideConsumeExample : Component
{
public override Element Render()
{
return VStack(12,
TextBlock("Outside: no provider"),
VStack(12,
Component<Greeting>()
).Provide(Contexts.UserName, "Alice")
).Padding(24);
}
}
class Greeting : Component
{
public override Element Render()
{
var name = UseContext(Contexts.UserName);
return TextBlock($"Hello, {name}!").FontSize(20).Bold();
}
}.Provide() is a modifier like .Padding() or .Background() (see
Styling and Theming) — it works on any element. The value
propagates to every descendant, no matter how deep.
A common use case is a theme toggle that affects an entire subtree. Here the root component provides a theme value, and child components consume it:
class ThemeSwitchExample : Component
{
public override Element Render()
{
var (isDark, setIsDark) = UseState(false);
var mode = isDark ? "dark" : "light";
return VStack(16,
ToggleSwitch(isDark, setIsDark, onContent: "Dark", offContent: "Light"),
VStack(12, Component<ThemePanel>()).Provide(Contexts.ThemeMode, mode)
).Padding(24);
}
}
class ThemePanel : Component
{
public override Element Render()
{
var theme = UseContext(Contexts.ThemeMode);
var elTheme = theme == "dark" ? ElementTheme.Dark : ElementTheme.Light;
return Border(
VStack(8,
TextBlock($"Current theme: {theme}").Bold(),
TextBlock("Panel adapts to context.").Foreground(Theme.SecondaryText)
).Padding(16)
).Background(Theme.CardBackground)
.CornerRadius(8)
.Set(b => b.RequestedTheme = elTheme);
}
}The ThemePanel component reads the context and applies ElementTheme
accordingly. When the toggle changes the provided value, all consumers
re-render with the new theme.
A child provider overrides its parent's value for its own subtree. Siblings keep the original value:
class NestedOverrideExample : Component
{
public override Element Render()
{
return HStack(16,
VStack(8,
Caption("Parent value"),
Component<NameDisplay>()
).Provide(Contexts.UserName, "Alice"),
VStack(8,
Caption("Overridden child"),
VStack(4, Component<NameDisplay>())
.Provide(Contexts.UserName, "Bob")
).Provide(Contexts.UserName, "Alice")
).Padding(24);
}
}
class NameDisplay : Component
{
public override Element Render()
{
var name = UseContext(Contexts.UserName);
return TextBlock(name).FontSize(18).SemiBold().Foreground(Theme.Accent);
}
}The inner .Provide() only affects descendants of that element. The sibling
subtree still sees the parent's value. This lets you create local overrides
without affecting the rest of the tree.
Components can provide and consume multiple contexts simultaneously. Each context is independent:
class MultipleContextsExample : Component
{
public override Element Render()
{
return VStack(8,
Component<ProfileCard>()
).Provide(Contexts.UserName, "Charlie")
.Provide(Contexts.FontScale, 22)
.Padding(24);
}
}
class ProfileCard : Component
{
public override Element Render()
{
var name = UseContext(Contexts.UserName);
var fontSize = UseContext(Contexts.FontScale);
return Border(
VStack(8,
TextBlock(name).FontSize(fontSize).Bold(),
TextBlock($"Font scale from context: {fontSize}px")
.Foreground(Theme.SecondaryText)
).Padding(16)
).Background(Theme.CardBackground).CornerRadius(8);
}
}Each ReactorContext<T> is a separate channel. Providing one doesn't affect
another. A component can call UseContext as many times as it needs.
Use context for cross-cutting concerns. Theme, locale, auth state, and feature flags are good candidates. Props are better for component-specific data.
Always set a meaningful default. Components that read context should work even without a provider — the default enables standalone testing and preview.
Keep context values immutable. Provide a new value to trigger re-renders. Mutating the existing object won't notify consumers.
Don't overuse context. If data only flows one or two levels down, pass it as props. Context is for data that many components at different depths need.
Combine with UseState for dynamic context. Store the value
in state at the provider level, and consumers update automatically when you
call the setter.
- Commanding — Previous: bundle actions with labels, icons, and keyboard accelerators
- Accessibility — Next: add screen reader support and keyboard navigation
- Hooks — Learn about UseContext, UseState, and other hooks that power context
- Styling and Theming — Use context to propagate theme values across your app
- Effects and Lifecycle — Trigger side effects when context values change



