As I understand it, you're attempting to conditionally present server components based on some state within a client component. Next.js recommends passing server components as props to client components, which is sort of what you were attempting to do with the children
prop. The problem with this approach is that the client component (in this case, <Search>
) does not have access to any of the data associated with its individual children
which it would need in order to perform filtering logic.
Since JSX can be passed as any prop, not just children
, I recommend doing the following:
// ServerListItem.tsx
type ServerListItemProps = {
message: string;
};
export default function ServerListItem({ message }: ServerListItemProps) {
return <li>{message}</li>;
}
// ServerList.tsx
import ClientList from './ClientList';
import ServerListItem from './ServerListItem';
export default async function ServerList() {
const data = await getData();
const listItems = data.map((listItem) => ({
message: listItem.message,
content: <ServerListItem key={listItem.id} message={listItem.message} />,
}));
return <ClientList listItems={listItems} />;
}
const getData = () => Promise.resolve(serverData);
const serverData = [
{
id: 1,
message: 'Item 1',
},
{
id: 2,
message: 'Item 2',
},
{
id: 3,
message: 'Item 3',
},
];
// ClientList.tsx
'use client';
import React from 'react';
type ClientListProps = {
listItems: {
message: string;
content: React.ReactNode;
}[];
};
export default function ClientList({ listItems }: ClientListProps) {
const [filterInput, setFilterInput] = React.useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFilterInput(e.target.value);
};
const filteredItems = listItems
.filter((listItem) => listItem.message.includes(filterInput))
.map((listItem) => listItem.content);
return (
<>
<input onChange={handleChange} placeholder="filter" />
<ul>{filteredItems}</ul>
</>
);
}
In this example, the <ServerList>
component is responsible for telling React to render each <ServerListItem>
component on the server and the props it should receive. It then passes a list of objects to <ClientList>
, each containing the server-rendered result of <ServerListItem>
as well as the message
to be used for filtering. <ClientList>
knows it will receive the list of objects and can perform client-side filtering to determine which objects' content
to display.
You would then render the <ServerList>
component in your page.tsx
file in app/
:
// page.tsx
import ServerList from './ServerList'
export default function Home() {
return (
<main>
<ServerList />
</main>
);
}