From fe4dee88bbc5f2d16bb2f435d6e3e096627d0f9d Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Mon, 20 Oct 2025 15:18:53 -0700 Subject: [PATCH 1/2] Add guide on using React context --- ...g-react-context-to-avoid-prop-drilling.mdx | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx diff --git a/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx b/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx new file mode 100644 index 0000000..1a5b718 --- /dev/null +++ b/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx @@ -0,0 +1,145 @@ +--- +title: Using React Context to Avoid Prop-Drilling +--- + +React Context is a powerful tool for sharing data across deeply nested components without having to pass props through every level of the tree. When building TypeScript-powered circuit design tools, context lets you centralize configuration and provide well-typed data to any component that needs it. + +## When to Reach for Context + +Use context when multiple components need the same piece of state or configuration. Common examples in tscircuit projects include: + +- Design-wide metadata such as board dimensions or authoring info +- User preferences like measurement units or default footprints +- Shared services (e.g., logger, analytics, or websocket clients) + +By colocating these values in a context provider, you avoid repetitive prop threading and keep your component interfaces focused on their primary responsibilities. + +## A Typed Context for Board Settings + +The following example shares board-level configuration with any component that needs it. The `BoardSettings` interface ensures every consumer receives a strongly typed object. + +```tsx +import { ReactNode, createContext, useContext, useMemo } from "react" + +type BoardSettings = { + boardName: string + boardSize: { width: number; height: number } + defaultFootprints: { + resistor: string + capacitor: string + } +} + +const BoardSettingsContext = createContext(null) + +export const BoardSettingsProvider = ({ + children, + value, +}: { + children: ReactNode + value: BoardSettings +}) => { + const memoizedValue = useMemo(() => value, [value]) + + return ( + + {children} + + ) +} + +export const useBoardSettings = () => { + const context = useContext(BoardSettingsContext) + + if (!context) { + throw new Error("useBoardSettings must be used within a BoardSettingsProvider") + } + + return context +} +``` + +### Why memoize the value? + +Passing a memoized value prevents unnecessary re-renders of components consuming the context. This becomes important when the provider sits high in your component tree and wraps many children. + +## Consuming the Context + +Components can now read the shared configuration without receiving it through props. + +```tsx +const ResistorList = ({ names }: { names: string[] }) => { + const { + defaultFootprints: { resistor }, + } = useBoardSettings() + + return ( + + {names.map((name) => ( + + ))} + + ) +} + +const DecouplingCapacitor = ({ name }: { name: string }) => { + const { + defaultFootprints: { capacitor }, + } = useBoardSettings() + + return +} +``` + +The components above use the shared default footprints without adding extra props to every layer above them. + +## Putting It All Together + +Wrap the portion of your circuit tree that needs access to the context with the provider. Any component rendered inside the provider can call `useBoardSettings()`. + +```tsx +export const InstrumentPanel = () => ( + + + + + + + + +) +``` + +Because every component inside the provider shares the same context, you can introduce additional consumers (for example, status displays or documentation overlays) without modifying intermediate components. + +## Testing Context Consumers + +When unit testing components that depend on the context, render them with the provider to supply the necessary data. Libraries like [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro/) make this pattern straightforward: + +```tsx +render( + + + +) +``` + +This keeps tests realistic while preserving the benefits of type safety. + +## Key Takeaways + +- Define a context value type that captures the shared configuration. +- Export both the provider and a custom hook that validates usage. +- Memoize the context value to avoid unnecessary renders. +- Wrap only the subtree that needs the shared data, keeping providers focused and intentional. + +With these patterns, React Context becomes a reliable way to manage shared state in your TypeScript tscircuit projects without the noise of prop drilling. From a93efc2d6700c08020f7f4c6d1ae04c195f240fe Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Mon, 20 Oct 2025 16:47:34 -0700 Subject: [PATCH 2/2] Update using-react-context-to-avoid-prop-drilling.mdx --- .../using-react-context-to-avoid-prop-drilling.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx b/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx index 1a5b718..1bbb680 100644 --- a/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx +++ b/docs/guides/typescript-guide/using-react-context-to-avoid-prop-drilling.mdx @@ -4,7 +4,7 @@ title: Using React Context to Avoid Prop-Drilling React Context is a powerful tool for sharing data across deeply nested components without having to pass props through every level of the tree. When building TypeScript-powered circuit design tools, context lets you centralize configuration and provide well-typed data to any component that needs it. -## When to Reach for Context +## When to use React Context Use context when multiple components need the same piece of state or configuration. Common examples in tscircuit projects include: