astro-static-context
v0.1.0
Published
Static render-time context helpers for Astro components.
Readme
Astro Static Context
astro-static-context is a small render-time context helper for Astro
components. It gives static Astro pages a familiar createContext() and
useContext() shape without adding client-side state or a UI framework.
Use it when Astro components need to coordinate while rendering static HTML at build time. It is server-render-only and does not run in browser scripts.
Installation
npm install astro-static-contextastro is a peer dependency. This package currently targets Astro 6 and Node
22.12 or newer.
Basic Usage
Create contexts in a shared module so providers and consumers import the same context object:
// theme-context.ts
import { createContext } from "astro-static-context";
export const ThemeContext = createContext({ theme: "light" });The returned context object is renderable as an Astro component:
---
import Child from "./Child.astro";
import { ThemeContext } from "./theme-context";
---
<ThemeContext value={{ theme: "dark" }}>
<Child />
</ThemeContext>Read the value from Astro frontmatter or from render-time helpers called by Astro components:
---
import { useContext } from "astro-static-context";
import { ThemeContext } from "./theme-context";
const { theme } = useContext(ThemeContext);
---
<p>Current theme: {theme}</p>useContext(Context) returns the nearest provider value for that context. If no
provider appears above the caller in the rendered Astro component tree, it
returns the default value passed to createContext().
Rendering Slots With Context
Some Astro components need to render a slot first, inspect state mutated by
children, and then render parent markup. Use Context.renderSlot(value,
Astro.slots, slotName?) for that pattern:
---
import { ThemeContext } from "./theme-context";
let title = "default";
const state = { setTitle: (t: string) => { title = t; } };
const Rendered = await ThemeContext.renderSlot(state, Astro.slots);
---
<section>
<h2>{title}</h2>
<Rendered />
</section>renderSlot() eagerly renders the slot under the provided context value, then
returns an Astro component that emits the cached HTML. The optional slotName
argument defaults to "default":
---
const SidebarHTML = await ThemeContext.renderSlot(state, Astro.slots, "sidebar");
---Pass Astro.slots explicitly. Astro is scoped to the currently rendering
component and cannot be read safely as a module global.
Nested Providers
Nested providers override outer providers for their subtree only:
<ThemeContext value={{ theme: "light" }}>
<Summary />
<ThemeContext value={{ theme: "dark" }}>
<Details />
</ThemeContext>
</ThemeContext>In this example, Summary reads { theme: "light" } and Details reads
{ theme: "dark" }.
Multiple contexts are independent because each returned context object is used as its own key.
Default Values
The default value is a fallback for components rendered without a provider:
const PricingContext = createContext({ currency: "USD" });Provider values intentionally win even when they are undefined:
<MaybeContext value={undefined}>
<Child />
</MaybeContext>Child receives undefined, not the default value. This keeps "no provider"
distinct from "a provider supplied an undefined value."
TypeScript
The default value controls the inferred context type:
const CounterContext = createContext({ value: 0 });
const counter = useContext(CounterContext);
counter.value.toFixed();If the value can be missing, include that in the type:
type CurrentUser = { name: string } | undefined;
const CurrentUserContext = createContext<CurrentUser>(undefined);The provider prop is typed as value: T, and useContext() returns T.
Do not call the returned context object like a normal JavaScript function. It is an Astro component factory and should be rendered in Astro markup.
Static Rendering Model
This helper is intentionally narrower than React context:
- Values exist only while Astro renders static HTML.
- Changing a value does not rerender anything in the browser.
- Context cannot cross into client-side scripts or hydrated islands.
- Context is scoped with Node
AsyncLocalStorage, so parallel static renders keep provider values isolated.
Use it for static render-time coordination between Astro components, not for interactive application state.
Unsupported React API
Context.Provider and Context.Consumer are not implemented. Render the
context object directly as an Astro component, and read values with
useContext() during server rendering.
