4. Polish
Add search and filters, custom menu icons, and a dashboard home page.
You have a working CRUD admin. Now let's make it feel finished: a search box, a custom menu icon, and a dashboard home page.
Adding search and filters to the list
The list controller already sorts and paginates. To show pagination controls,
render <ListPagination /> below the table. To let users search, add filter
inputs to the list's actions slot with <FilterForm> and <FilterButton>.
Chelone has no react-admin filters prop on <List> — filtering is plain
composition. Declare an array of filter inputs, then render <FilterForm> for
the visible inputs and <FilterButton> to toggle optional filters. Merge the
imports into src/posts.tsx and replace PostList; keep the create, edit, and
show views from the previous step:
import {
DataTable,
FilterButton,
FilterForm,
List,
ListPagination,
ReferenceInput,
SearchInput,
SelectInput,
} from "@chelone/react";
const postFilters = [
<SearchInput source="q" alwaysOn key="q" />,
<ReferenceInput source="author_id" reference="authors" key="author_id">
<SelectInput label="Author" optionText="name" />
</ReferenceInput>,
];
export function PostList() {
return (
<List
actions={
<>
<FilterForm filters={postFilters} />
<FilterButton filters={postFilters} />
</>
}
>
<DataTable>
<DataTable.Col source="title" />
{/* …the other columns… */}
</DataTable>
<ListPagination />
</List>
);
}The q filter is an alwaysOn search — Chelone forwards it to your data
provider's getList under filter.q. The wrapper from
Setup matches a case-insensitive
substring of the post's title or body before pagination. Type On to find
"On Computable Numbers". A production provider must implement its own q
search. The author_id filter is optional: the <FilterButton> dropdown toggles
it on, and because it's a <ReferenceInput> its <SelectInput> is already
populated with the authors.
FilterButton toggles visibility; FilterForm renders the search box and any
enabled filters. Both use the same postFilters array.
Customizing the menu icon
The sidebar shows a default icon for every resource. Pass an icon to a
<Resource> to change it — any React component works, so a Lucide icon drops
straight in. First define an authors list:
import { DataTable, List, ListPagination } from "@chelone/react";
export function AuthorList() {
return (
<List>
<DataTable>
<DataTable.Col source="name" />
</DataTable>
<ListPagination />
</List>
);
}The following App.tsx snippets update the App from
Setup: keep its dataProvider,
queryClient, resources, view imports, and surrounding <BrowserRouter>.
import { Admin, Layout, Resource } from "@chelone/react";
import { StickyNote, Users } from "lucide-react";
import { AuthorList } from "./authors";
// …
<Admin dataProvider={dataProvider} queryClient={queryClient} resources={resources}>
<Admin.Layout>
<Layout />
</Admin.Layout>
<Resource name="posts" icon={StickyNote} list={PostList} edit={PostEdit} create={PostCreate} show={PostShow} />
<Resource name="authors" icon={Users} list={AuthorList} />
</Admin>;Using a custom home page
By default <Admin> shows the first resource's list as the home page. To show
your own component instead, pass it as the dashboard prop:
export function Dashboard() {
return (
<section>
<h1>Welcome to the administration</h1>
<p>Manage posts and authors from the sidebar.</p>
</section>
);
}import { Admin } from "@chelone/react";
import { Dashboard } from "./dashboard";
<Admin dataProvider={dataProvider} queryClient={queryClient} resources={resources} dashboard={<Dashboard />}>
{/* …resources… */}
</Admin>;Next: auth & production.