Wrapper Component

If you use the list component in your app often with different entities/queries, it’s a good idea to create a reusable wrapper component with defaults for your app. Below is an example from the demo website that uses Mantine and Tanstack Router.

  • Extend IDBList props to allow full customization
  • Styles are automatically applied to all lists
  • Populates default skeleton and no results UI components
  • Take in a route param to allow clicking on an item to navigate to a detail view
import { ScrollArea } from '@mantine/core';
import { Skeleton } from '@mantine/core';
import { Link } from '@tanstack/react-router';

import type { RouteFullPaths } from '~client/lib/sidebar';
import type { SearchParams } from '~client/lib/utils';
import { IDBList, IDBListProps, IDBEntityType, IDBQueryType, IDBSchemaType } from 'instantdb-react-ui';

/** Extends IDBListProps with our custom wrapper props */
type ListWrapperProps<
	TSchema extends IDBSchemaType,
	TEntity extends IDBEntityType<TSchema>,
	TQuery extends IDBQueryType<TSchema>,
> = IDBListProps<TSchema, TEntity, TQuery> & {
	route: RouteFullPaths
	itemId?: string
	search?: SearchParams
};

/**
 * Reusable wrapper component for IDB List
 * Adds navigation links, skeleton, no results, etc.
 */
export function List<
	TSchema extends IDBSchemaType,
	TEntity extends IDBEntityType<TSchema>,
	TQuery extends IDBQueryType<TSchema>,
>({ itemId, search, route, ...idbListProps }: ListWrapperProps<TSchema, TEntity, TQuery>) {
	return (
		<ScrollArea className="list-scrollarea">
			<IDBList<TSchema, TEntity, TQuery>
				{...idbListProps}

				// Defaults
				skeleton={idbListProps.skeleton ?? <ListSkeleton />}
				noResults={idbListProps.noResults ?? <p className="text-gray-500">No results found</p>}

				// Render
				render={(item, id) => (
					<Link
						key={id}
						to={route}
						params={{ id }}
						className="list-item"
						data-selected={id === itemId}
						search={search}
					>
						{idbListProps.render(item, id)}
					</Link>
				)}
			/>
		</ScrollArea>
	);
}

/** Skeleton loading UI for the list */
function ListSkeleton() {
	return (
		<>
			{Array.from({ length: 10 }).map((_, i: number) => (
				<Skeleton key={i} height={42} className="list-item" />
			))}
		</>
	);
}

export default List;