Chapter 2
Advanced Component Patterns
Beyond the basics lies a world of compositional ingenuity-where SolidJS's fine-grained reactivity enables advanced architectures, high cohesion, and maximal code reuse. This chapter invites readers to master sophisticated component design patterns that unlock scalable, expressive UIs. Traverse the landscape of context, advanced composition, local-global coordination, and dynamic runtime structures, all tailored for high-performance, maintainable applications.
2.1 Container and Presentational Components
The architectural paradigm of dividing user interface components into container and presentational forms remains a fundamental design approach for managing complexity in modern frontend applications. In the context of SolidJS, this separation takes on nuanced characteristics due to the library's unique reactive system centered on fine-grained reactivity, signals, and stores. Understanding how to effectively isolate business logic from view definitions using these primitives is key to achieving modularity and maintainability in large-scale SolidJS applications.
Container components serve as the stateful drivers of the application's logic and data flow. They typically perform asynchronous data fetching, manage state transitions, and orchestrate user interactions. In SolidJS, containers leverage signals or stores to encapsulate stateful information. Signals, created via the createSignal API, provide a reactive getter/setter pair that emits updates when the contained value changes. Stores, introduced through createStore, offer a more complex state structure capable of representing nested objects with structural sharing and recursive reactivity. Emphasizing the use of signals or stores at the container level facilitates separation from presentation concerns, as these components act as the primary data sources.
Presentational components, by contrast, are predominantly stateless and devoted to rendering the user interface based on props. They do not fetch data or modify application state directly but instead receive data and event handlers from their container counterparts. In SolidJS, presentational components can be implemented as simple functions that destructure props or as small compositions of JSX, relying on the reactivity system to automatically update the DOM when data passed from container components changes. This statelessness dramatically simplifies testing and reuse, as presentational components have no external dependencies or side effects.
Isolating business logic within container components, while confining view and markup definitions to presentational components, yields several notable benefits. First, by localizing data fetching and state mutations in containers, side effects are minimized in the view layer, reducing unpredictable behavior and rendering complexity. Second, it allows business logic to be refactored, optimized, or replaced without impacting presentation code, enhancing the adaptability of the UI. Third, controlled reactivity through signals and stores enables granular updates, limiting rerendering and improving performance.
To illustrate effective stratification, consider a container component responsible for fetching a list of items from an external API and managing selection state:
import { createSignal, createEffect } from 'solid-js'; function ItemsContainer() { const [items, setItems] = createSignal([]); const [selectedId, setSelectedId] = createSignal(null); createEffect(() => { fetch('/api/items') .then(r => r.json()) .then(data => setItems(data)); }); const selectItem = id => setSelectedId(id); return <ItemsList items={items()} selectedId={selectedId()} onSelect={selectItem} />; } Here, ItemsContainer abstracts away all asynchronous data fetching and state management. It exposes a clean API to ItemsList, the presentational component, passing reactive values and callbacks. ItemsList can be a simple stateless renderer:
function ItemsList({ items, selectedId, onSelect }) { ...