In my next.js 14 project, I need to pass a state from a child component to the parent component.
Currently, I declared the state in the parent component and passed it to children. ( thus making everything under the parent component client side)
But I want to keep things server component when possible. Here’s a minimal reproducible example of what I’m trying to do:
The state I have is isFocused
. The navbar has a button to toggle it. When it’s enabled. The whole website is just,
<Navbar/>
<FocusedPage />
If not enabled,
<Navbar/>
<Hero />
<FocusedPage />
...
Now whether to render the <Hero />
component or not, I need to know if isFocused
is true
or false
.
So, I’m keeping it under the client parent component. Thus, <Hero />
also becomes a client side component.
2
Answers
What you are doing is called "Lifting state up": https://react.dev/learn/sharing-state-between-components
There is no way to pass state from child to parent without it. Data flow in react is unidirectional.
Others solution for your case are:
First, you should know that it’s not a bad practice to use client components, they exist for a reason, and that reasons are mentioned in NextJS Docs:
Also, as per Lee Robinson from Vercel:
However, Sometimes (like in your situation), you need to keep everything server component but the interactivity part to be a client component.
Lifting state up from a child client component (because of the
onClick
event listener) to the parent server component is not an option and it will throw Runtime Error:Solution
The most common way to "lift the state up" in NextJS is by using URLSearchParams you will use it in many situations especially when you want to pass data from client to server, but that’s not the only use case though.
To take advantage of URLSearchParams, you have to tweak your code a little bit.
Currently, you have Four components and one
page.tsx
all of the components will be server components except theNavbar.tsx
, you will put your state inNavbar.tsx
instead ofHome.tsx
because that component is responsible for changing state, also will have event listener to change the state and add the query param to the URL to use it later in server components.Navbar.tsx
By default, page.tsx recives
searchParams
which is an object containing all query params, you can pass it tohome.tsx
:page.tsx
JSON.parse
needed to convert true/false fromstring
toboolean
since everything you get from the url isstring
.searchParams?.focusMode ?? false
is needed because first render you will not have anysearchParams
so it will returnundefined
.Finally, this how you
Home.tsx
will look like:Home.tsx
all other files remained as they were.
Bonus
If you want to make sure that all components are server components, try
console.log(window)
in any of them and you will see error (except forNavbar.tsx
because it’s a client component):Check out the working example on StackBlitz