useSearchParam in 'use client' throws suspense error, docs examples always throw errors
Answered
Greenish Elaenia posted this in #help-forum
Greenish ElaeniaOP
https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
The docs provide this example:
'use client'
import { useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
function Search() {
const searchParams = useSearchParams()
return <input placeholder="Search..." />
}
export function Searchbar() {
return (
<Suspense>
<Search />
</Suspense>
)
}
This is not helpful at all - I'm trying to get the string value in search param. Like token=ABC123. I want the ABC123.
Why is it returning a react.node / html? How do you pass the value into an onclick later for example, if you want to have a param like return_url=www.previouswebsite.com, or query strings?
The docs provide this example:
'use client'
import { useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
function Search() {
const searchParams = useSearchParams()
return <input placeholder="Search..." />
}
export function Searchbar() {
return (
<Suspense>
<Search />
</Suspense>
)
}
This is not helpful at all - I'm trying to get the string value in search param. Like token=ABC123. I want the ABC123.
Why is it returning a react.node / html? How do you pass the value into an onclick later for example, if you want to have a param like return_url=www.previouswebsite.com, or query strings?
Answered by Havana
I guess it wants you to make it be wrapped inside Suspense inside the parent component, not inside the component itself
98 Replies
Greenish ElaeniaOP
The code I'm trying to debug is a close button at the top right corner in a Navbar component
'use client'
export const Navbar = ({
}: Props) => {
function ReadReturnUrl() {
const searchParams = useSearchParams()
const returnUrl = searchParams.get('return_url');
// This does not solve the error AND blocks deployment
return returnUrl;
}
return (
<div>
{/* Other irrelevant html /}
{/ Close button */}
<Suspense fallback={#Unknown Channel</>}>
<button
onClick={() => {
window.parent.location.href =
}}>
Close
</button>
</Suspense>
</div>
)
}
export const Navbar = ({
}: Props) => {
function ReadReturnUrl() {
const searchParams = useSearchParams()
const returnUrl = searchParams.get('return_url');
// This does not solve the error AND blocks deployment
return returnUrl;
}
return (
<div>
{/* Other irrelevant html /}
{/ Close button */}
<Suspense fallback={#Unknown Channel</>}>
<button
onClick={() => {
window.parent.location.href =
${ ReadReturnUrl() };}}>
Close
</button>
</Suspense>
</div>
)
}
Greenish ElaeniaOP
And if you look at this docs, https://nextjs.org/docs/app/api-reference/functions/use-search-params, this docs example syntax copy paste literally throws the previously linked error in deployment
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// URL ->
//
return #Unknown ChannelSearch: {search}</>
}
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// URL ->
/dashboard?search=my-project//
search -> 'my-project'return #Unknown ChannelSearch: {search}</>
}
"useSearchParams() should be wrapped in a suspense boundary at page "/". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout"
Greenish ElaeniaOP
'use client'
export const Navbar = ({
}: Props) => {
const searchParams = useSearchParams();
const return_url = searchParams.get('return_url');
return (
<div>
{/* Other irrelevant html /}
{/ Close button */}
<Suspense fallback={#Unknown Channel</>}>
<button
onClick={() => {
window.parent.location.href =
}}>
Close
</button>
</Suspense>
</div>
)
}
Changed the code to pass const variable straight into the html section - This also fails, but is exactly the same syntax as the example, i thought
export const Navbar = ({
}: Props) => {
const searchParams = useSearchParams();
const return_url = searchParams.get('return_url');
return (
<div>
{/* Other irrelevant html /}
{/ Close button */}
<Suspense fallback={#Unknown Channel</>}>
<button
onClick={() => {
window.parent.location.href =
${ return_url };}}>
Close
</button>
</Suspense>
</div>
)
}
Changed the code to pass const variable straight into the html section - This also fails, but is exactly the same syntax as the example, i thought
Havana
Do you also have to set it? Or only get
Greenish ElaeniaOP
Only get and put it into a onclick
Havana
Why not use SSR component then?
It has a built-in searchParams prop
You could do that, and then wrap the client component with the onclick button inside
and pass the value to it
Greenish ElaeniaOP
Oh Im unfamiliar with this, turns out Im using the wrong tool for the job?
Havana
No no, it is fine to use it, but since it errors, for unknown reasons, you can use SSR
export default async function ProfilePage({
params,
searchParams,
}: {
params: { id: string };
searchParams: { category: string };
}) {Here is an example
Greenish ElaeniaOP
Sorry what does SSR stand for? Or what should I be searching in the docs to get
Thank you!
@Greenish Elaenia Sorry what does SSR stand for? Or what should I be searching in the docs to get
Havana
Server side rendering (server component)
Cimarrón Uruguayo
tbh you don't need hooks at all for this
Cimarrón Uruguayo
unnecessarily making it a client component
just make it a server component
Havana
if he only gets, no point
Cimarrón Uruguayo
yep
Havana
But if you will need to set it later on, you can wrap a client component inside this SSR component, and just setparams, then router.refresh()
it will do the trick
Cimarrón Uruguayo
oh like modify it to add additional filters
Greenish ElaeniaOP
Does it matter that the parent to this component is a client component?
Cimarrón Uruguayo
yeah that works :)
@Greenish Elaenia Does it matter that the parent to this component is a client component?
Havana
If the SSR component is async it won't work
What do you mean by parent?
Like, it wraps this specific SSR component?
Like, it wraps this specific SSR component?
Greenish ElaeniaOP
Currently it's page.tsx (ssr) -> a client component -> this navbar component
Havana
Yeah thats problematic
Why would you do it though?
Cimarrón Uruguayo
why not put the navbar into the page.tsx file
Havana
yeah, wrap it in the page.tsx
both client and ssr navbar
Greenish ElaeniaOP
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#unsupported-pattern-importing-server-components-into-client-components This is exactly how navbar is put in rn
'use client'
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
#Unknown Channel
<button onClick={() => setCount(count + 1)}>{count}</button>
<ServerComponent />
</>
)
}
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
#Unknown Channel
<button onClick={() => setCount(count + 1)}>{count}</button>
<ServerComponent />
</>
)
}
Cimarrón Uruguayo
yep that won't work
Greenish ElaeniaOP
Ok so I need to turn navbar into a SSR, and put it into the page.tsx (which is a SSR)?
Havana
wait so navbar is a client?
so I see no issue in wrapping client inside client
Cimarrón Uruguayo
navbar is already a ssr (if you haven't removed the hooks already), and make it a direct child of page.tsx
Havana
so whats the question 😄
ofc u can
Cimarrón Uruguayo
ok since its a client component you won't face issues
Havana
so a parent ssr component that wraps a cl component that wraps another cl?
Greenish ElaeniaOP
Yes ssr > cl > cl
Havana
Ur good to go
Make sure that the searchParams props are inside the SSR component
Greenish ElaeniaOP
Idk why im getting this suspense error, I guess thats the question
Havana
And then just pass it to the other components if u need
@Greenish Elaenia 'use client'
export const Navbar = ({
}: Props) => {
const searchParams = useSearchParams();
const return_url = searchParams.get('return_url');
return (
<div>
{/* Other irrelevant html */}
{/* Close button */}
<Suspense fallback={<></>}>
<button
onClick={() => {
window.parent.location.href = `${ return_url }`;
}}>
Close
</button>
</Suspense>
</div>
)
}
Changed the code to pass const variable straight into the html section - This also fails, but is exactly the same syntax as the example, i thought
Greenish ElaeniaOP
this code here fails to deploy throwing this error still, "useSearchParams() should be wrapped in a suspense boundary at page "/". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout"
Havana
I guess it wants you to make it be wrapped inside Suspense inside the parent component, not inside the component itself
Answer
Havana
page -> ssr
parent cl - wrap the navbar with suspense
navbar cl
parent cl - wrap the navbar with suspense
navbar cl
Cimarrón Uruguayo
yeah maybe try having the suspense outside the navbar component
Greenish ElaeniaOP
Interesting lemme try it out, appreciate u both so much
Havana
yes, because it literally says wrap the useSearchParams, not the component
so it has to be wrapped in the parent level
Cimarrón Uruguayo
u can either do that ^, OR make a navbarLoading.tsx + navbarContent.tsx
and have navbar.tsx with the suspense
just ur choice of where u want to put the Suspense
that's all
Havana
@Greenish Elaenia can it be related to the fact that you fetch something?
Make any promise at all? (async)
Make any promise at all? (async)
On the parent level or something
@Havana <@232270422509486091> can it be related to the fact that you fetch something?
Make any promise at all? (async)
Greenish ElaeniaOP
The parent level is kind of insane with a ton of sockets and fetch inside useEffect, etc
If I comment out and just feed a constant string "www.google.com" or something to the navbar's onclick that works fine
If I comment out and just feed a constant string "www.google.com" or something to the navbar's onclick that works fine
Havana
Well that explains it
Greenish ElaeniaOP
What explains what?
@Havana I guess it wants you to make it be wrapped inside Suspense inside the parent component, not inside the component itself
Greenish ElaeniaOP
OMG?! it worked
@Greenish Elaenia OMG?! it worked
Havana
Im glad
@Greenish Elaenia What explains what?
Havana
Well, obviously, if you do any async stuff on a top level and you except the useSearchParams to work, its not gonna happen
Because you mutate the searchParams, I guess on the top level, but the child still doesn't have any access to it
Greenish ElaeniaOP
@Havana @Cimarrón Uruguayo tysm its been blocking me for 2 days now, next time youre in Taipei, Taiwan. plz message me, dinner and drinks on me
Havana
So you have to wrap it inside suspense to make sure the component (child), loads only after all the logic has passed
Cimarrón Uruguayo
nws bro
@Havana So you have to wrap it inside suspense to make sure the component (child), loads only after all the logic has passed
Greenish ElaeniaOP
so the block is due to the parent doing the async awaiting, so it is the component that needs Suspense
and navbar in this case is like a readonly sort of situation?
So, adding suspense in the navbar doesnt do anything..
It is actually a rare case, because useSearchParams works independently of any async stuff, it works in a sync mode, it just reads url params, but I guess in this specific case it does require waiting
Greenish ElaeniaOP
'rare case' ok i dont feel so dumb now
Havana
Maybe you are making any async operations that affect the params?
Greenish ElaeniaOP
not that i know of, the one-page app doesnt change the url until people leave the site
Havana
But when do you change it? In the navbar?
Greenish ElaeniaOP
the navbar takes a prop string that is set by a socket.on("whatever", function...)
oh..
💡 i see now
so the socket needs to be executed, any only then render the navbar with the stuff you need
Yep, a rare case
Greenish ElaeniaOP
that makes so much sense now
Havana
Now u know
Greenish ElaeniaOP
really appreciate you
Havana
