Dynamic Rendering
Introduction
Dynamic rendering is the process of updating the visual representation of a component based on signals.
Rendering Dynamic Content
Dynamic content in sig
is achieved by using some of the primitives provided by the library in combination with signals.
If
component: Renders its condition branch based on the signal value.For
component: Render a list of items based on the signal that holds the list.Switch
,Case
&Default
components: Render the firstCase
component whosecondition
signal is truthy, otherwise render theDefault
component.Await
component: Render an asynchronous component or a lazy-loaded component (or both).
If
Component
The If
component is used to conditionally render content based on a signal value.
when the signal value is truthy the If
component will render the then
branch, otherwise it will render the fallback
(else) branch.
import { createSignal, If } from 'sig';
function MenuButton() { const [isMenuOpen$, setIsMenuOpen] = createSignal(false); return (<button className="..." onClick={() => setIsMenuOpen((curr) => !curr)}> <If condition={isMenuOpen$} then={<X className="block h-6 w-6" />} fallback={<Menu className="block h-6 w-6" />} /> </button>);}

For
Component
The For
component is used to render a list of items based on a signal that holds the list.
Here is an example of how to use the For
component to render a list of items:
The list display the filtered search result of the items based on the search term.
import { For, createSignal, combineLatest } from 'sig';
function SearchList() { const [items$, _setItems] = createSignal(['Apple', 'Banana', 'Orange', 'Mango', 'Pineapple']); const [searchTerm$, setSearchTerm] = createSignal(''); const filteredItems$ = combineLatest([items$, searchTerm$]) .derive<string[]>(([items, searchTerm]) => !searchTerm ? items : items .filter((item) => item.toLowerCase() .includes(searchTerm.toLowerCase())) ); return (<div className="..."> <input value={searchTerm$} onInput={(e) => setSearchTerm(e.target.value)} placeholder="Search items" className="..." /> <For as='div' asProps={{ className: "..." }} list={filteredItems$} empty={<div>No items</div>} factory={(item) => <div>{item}</div>} /> </div>);}

For
Item Indexing
The For
component manages indexing of the rendered items in the list, it is crucial that the “index” (key) of each item is unique in order handle changes in the list’s items and order.
The For
component will use the index
to track the items in the list, and update the items in the list when the list changes.
By providing an index
to the For
component, you can specify how to generate the key for each item.
When index
prop is provided :
- if it is a function
, it will be called with the item and the index of the item in the list.
- if it is a string
, it will be used as a property name to extract the key from the item object.
When no index
prop provided, the default indexing behavior is the following:
- If the item is a primitive value, the index will be the item value.
- If the item is an object
, the index will be the index of the item in the list.
Switch
, Case
& Default
Components
The Switch
, Case
& Default
components are used to render the first Case
component whose condition
signal is truthy, otherwise render the Default
component.
Here is an example of how to use the Switch
, Case
& Default
components to render a component based on the selected tab:
import { createSignal, Switch, Case, Default } from 'sig';
function TabContent() { const [selectedTab$, setSelectedTab] = createSignal('Tab1'); return (<div className="..."> <button onClick={() => setSelectedTab('Tab1')}>Tab1</button> <button onClick={() => setSelectedTab('Tab2')}>Tab2</button> <Switch condition={selectedTab$}> <Case value='Tab1'> <div>Tab1 content</div> </Case> <Case value='Tab2'> <div>Tab2 content</div> </Case> <Default> <div>No content</div> </Default> </Switch> </div>);}
Await
Component
The Await
component is used to render an asynchronous component or a lazy-loaded component (or both).
Basically the Await
component expect a component
field, that can be a :
- An async component function.
- Function that return a Promise
of an object with a default
field with a value of a component function.
Lets focus first on the lazy loading part first, This is useful when you want to load a component only when it is needed, and not including it unnecessarily in the main build.
Combining the Await
component with the import
function, we’re getting a powerful pattern, to ship only the necessary code to the client.
Here is an example of how to use the Await
component to render a lazy-loaded component:
import { Await, If } from 'sig';const lazyComponent = () => import('./LazyComponent');
function MainPage() { const [showLazyComponent$, setShowLazyComponent] = createSignal(false); return (<div> <button onClick={() => setShowLazyComponent((curr) => !curr)}>Toggle</button> <If condition={showLazyComponent$} then={<Await component={lazyComponent} /> </div>);}
In the above example, the If
component will render the Await
component only when the showLazyComponent$
signal is truthy,
and when the Await
component is rendered, it will load and render the LazyComponent
component.
When inspecting the network tab in the browser, you will see that the LazyComponent
build file, is loaded only when the button is clicked.
The Await
component can also be used to render an asynchronous component, which is useful when you need to fetch data asynchronously before rendering the component.
Here is an example of how to use the Await
component to render a component that fetches data asynchronously:
import { createSignal, Await } from 'sig';
async function AsyncUserCard() { const user = await fetch('/api/user').then((res) => res.json());
return (<div> <img src={user.avatar} alt={user.name} /> <div>{user.name}</div> </div>);}
function HomePage() { return (<div> <Await component={AsyncUserCard} /> </div>);}