Components
For

For Component

A flexible, type-safe React component that provides a powerful alternative to the JavaScript map() function with enhanced rendering capabilities.

Props

ForProps

PropTypeRequiredDefaultDescription
eachT[]Yes-The array of data to iterate over
children(item: T, index: number, array: T[]) => ReactNodeYes-Render function that returns a React node for each item
getKey(item: T, index: number) => string | numberNoundefinedCustom key extractor function
empty() => ReactNodeNoundefinedRender function for when the array is empty
loading() => ReactNodeNoundefinedRender function for when data is loading
isLoadingbooleanNofalseBoolean to indicate if data is loading
asElementTypeNo'div'Wrapper element or component type
wrapperPropsOmit<ComponentProps<E>, 'children'>No{}Props to pass to the wrapper element

Component

For.tsx
import React, { ComponentProps, ElementType, ReactNode } from "react";
 
/**
 * Props for the For component
 * @template T - The type of items in the data array
 * @template E - The element type for the wrapper component
 */
interface ForProps<T, E extends ElementType = "div"> {
  each: T[];
  children: (item: T, index: number, array: T[]) => ReactNode;
  getKey?: (item: T, index: number) => string | number;
  empty?: () => ReactNode;
  loading?: () => ReactNode;
  isLoading?: boolean;
  as?: E;
  wrapperProps?: Omit<ComponentProps<E>, "children">;
}
 
/**
 * A flexible component that works like Array.map() but with React-specific optimizations
 */
function For<T, E extends ElementType = "div">({
  each,
  children,
  getKey,
  empty,
  loading,
  isLoading = false,
  as,
  wrapperProps = {} as Omit<ComponentProps<E>, "children">,
}: ForProps<T, E>) {
  if (isLoading && loading) return <>{loading()}</>;
  if (each.length === 0 && empty) return <>{empty()}</>;
 
  const items = each.map((item, index, array) => {
    const key = getKey ? getKey(item, index) : index;
    return (
      <React.Fragment key={key}>{children(item, index, array)}</React.Fragment>
    );
  });
 
  if (as) {
    const Component = as;
    return React.createElement(Component, wrapperProps, items);
  }
 
  return <>{items}</>;
}
 
export default For;

Basic Usage

Simple Iteration

<For each={users}>{(user) => <UserCard user={user} />}</For>

Custom Key Extraction

<For each={users} getKey={(user) => user.id}>
  {(user) => <UserCard user={user} />}
</For>

Loading State

<For each={products} isLoading={isLoading} loading={() => <LoadingSpinner />}>
  {(product) => <ProductItem product={product} />}
</For>

Empty State

<For each={products} empty={() => <EmptyState message="No products found" />}>
  {(product) => <ProductItem product={product} />}
</For>

Wrapper Elements

<For each={todos} as="ul" wrapperProps={{ className: "todo-list" }}>
  {(todo) => <li>{todo.text}</li>}
</For>

Complete Example

function ProductList() {
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
 
  useEffect(() => {
    fetchProducts().then((data) => {
      setProducts(data);
      setIsLoading(false);
    });
  }, []);
 
  return (
    <For
      each={products}
      isLoading={isLoading}
      loading={() => <LoadingSpinner />}
      empty={() => <EmptyState message="No products found" />}
      as="div"
      wrapperProps={{ className: "products-grid" }}
    >
      {(product) => <ProductCard key={product.id} product={product} />}
    </For>
  );
}

Advanced Features

Index and Array Access

<For each={items}>
  {(item, index, array) => (
    <div>
      Item {index + 1} of {array.length}: {item.name}
    </div>
  )}
</For>

Conditional Rendering

<For each={items}>
  {(item) => (item.isVisible ? <VisibleItem item={item} /> : null)}
</For>

Nested Iteration

<For each={categories}>
  {(category) => (
    <div key={category.id} className="category">
      <h2>{category.name}</h2>
      <For each={category.items}>
        {(item) => <ItemCard key={item.id} item={item} />}
      </For>
    </div>
  )}
</For>

Performance Considerations

  • Provide a getKey function for stable data structures
  • Memoize child components to prevent unnecessary re-renders
  • Loading state prevents rendering before data is ready

TypeScript Support

Full TypeScript support with generic type inference:

interface User {
  id: number;
  name: string;
}
 
<For<User> each={users}>{(user) => <UserCard user={user} />}</For>;

Comparison with Array.map()

Array.map()

{
  users.map((user) => <UserCard user={user} />);
}

For Component

<For each={users}>{(user) => <UserCard user={user} />}</For>

Key Advantages

  • Flexible rendering
  • Built-in loading and empty states
  • Type-safe
  • Supports custom wrappers
  • Easy key management
  • Consistent rendering across different scenarios
Last updated on March 24, 2025