For
Declarative list rendering with built-in empty state handling for cleaner JSX.
The For component provides a declarative way to render lists and arrays in React, with automatic empty state handling and support for numeric ranges.
Installation
npm install @zayne-labs/ui-reactPreview
Loading...
Usage
Pass an array to each and utilize the render prop to define your items.
import { For } from "@zayne-labs/ui-react/common/for";
function UserList({ users }) {
return (
<For each={users} fallback={<p>No users joined yet.</p>}>
{(user) => (
<div key={user.id} className="p-2 border-b">
{user.name}
</div>
)}
</For>
);
}Pass a number to each to repeat content a specific number of times. Perfect for skeletons!
import { For } from "@zayne-labs/ui-react/common/for";
function TableSkeleton() {
return (
<For each={5}>
{(i) => (
<div key={i} className="h-8 w-full animate-pulse bg-gray-100 rounded" />
)}
</For>
);
}Use renderItem prop for cleaner syntax when you need to pass the render function as a prop.
import { ForWithWrapper } from "@zayne-labs/ui-react/common/for";
function ProductGrid({ products }) {
return (
<ForWithWrapper
className="grid grid-cols-3 gap-4"
each={products}
renderItem={(product) => (
<li key={product.id} className="p-4 border rounded">
<h3>{product.name}</h3>
<p>${product.price}</p>
</li>
)}
/>
);
}Component Source
import type { DiscriminatedRenderItemProps, PolymorphicPropsStrict,} from "@zayne-labs/toolkit-react/utils";import { isArray, isNumber, type Prettify } from "@zayne-labs/toolkit-type-helpers";type ArrayOrNumber = number | readonly unknown[];type GetArrayItemType<TArray extends ArrayOrNumber> = TArray extends readonly unknown[] ? TArray[number] : TArray extends number ? number : unknown;type RenderPropFn<TArray extends ArrayOrNumber> = ( item: GetArrayItemType<TArray>, index: number, array: Array<GetArrayItemType<TArray>>) => React.ReactNode;export type ForRenderProps<TArray extends ArrayOrNumber> = DiscriminatedRenderItemProps< RenderPropFn<TArray>>;/* eslint-disable perfectionist/sort-intersection-types -- Prefer the object to come first before the render props */export type ForProps<TArray extends ArrayOrNumber> = Prettify< { each: TArray; fallback?: React.ReactNode; } & ForRenderProps<TArray>>;/* eslint-enable perfectionist/sort-intersection-types -- Prefer the object to come first before the render props */const isArrayEmpty = <TArray extends ArrayOrNumber>(each: TArray) => { // eslint-disable-next-line ts-eslint/no-unnecessary-condition -- Allow return each == null || (isNumber(each) && each === 0) || (isArray(each) && each.length === 0);};export function For<const TArray extends ArrayOrNumber>(props: ForProps<TArray>) { const { children, each, fallback = null, renderItem } = props; if (isArrayEmpty(each)) { return fallback; } const resolvedArray = isNumber(each) ? [...Array(each).keys()] : (each as unknown[]); const selectedChildren = typeof children === "function" ? children : renderItem; const elementList = resolvedArray.map((...params) => { type Params = Parameters<RenderPropFn<TArray>>; return selectedChildren(...(params as Params)); }); return elementList;}export type ForWithWrapperProps< TArray extends ArrayOrNumber, TElement extends React.ElementType = "ul",> = PolymorphicPropsStrict<TElement, ForProps<TArray>> & { displayFallBackWhenEmpty?: boolean };export function ForWithWrapper< const TArray extends ArrayOrNumber, TElement extends React.ElementType = "ul",>(props: ForWithWrapperProps<TArray, TElement>) { const { as: ListContainer = "ul", children, displayFallBackWhenEmpty = false, each, fallback = null, renderItem, ...restOfProps } = props; if (displayFallBackWhenEmpty && isArrayEmpty(each)) { return fallback; } return ( <ListContainer {...restOfProps}> <For {...({ children, each, fallback, renderItem } as ForProps<TArray>)} /> </ListContainer> );}Component API
Examples
Semantic Table Rows
import { ForWithWrapper } from "@zayne-labs/ui-react/common/for";
function StatisticsTable({ data }) {
return (
<table>
<ForWithWrapper as="tbody" each={data}>
{(row) => (
<tr key={row.id}>
<td>{row.label}</td>
<td>{row.value}</td>
</tr>
)}
</ForWithWrapper>
</table>
);
}Last updated on