How to update a query param using next js 13 app directory #47583
-
SummaryIn this docs we don't have a way to update the query params, beacuse useSearchParams returns a read-only version of the URLSearchParams interface. https://beta.nextjs.org/docs/api-reference/use-search-params Is there a way to this? 'use client';
import { useSearchParams } from 'next/navigation';
const searchParams = useSearchParams();
searchParams.delete("page");
searchParams.set("page", "1"); Additional informationNo response ExampleNo response |
Beta Was this translation helpful? Give feedback.
Replies: 23 comments 53 replies
-
I think one way is to do this is to use the "use client";
import type { ChangeEvent } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
const options = ["mew", "mewtwo", "pikachu"];
export const DropDown = ({ selected }: { selected: string }) => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const onSelect = (event: ChangeEvent<HTMLSelectElement>) => {
// now you got a read/write object
const current = new URLSearchParams(Array.from(searchParams.entries())); // -> has to use this form
// update as necessary
const value = event.target.value.trim();
if (!value) {
current.delete("selected");
} else {
current.set("selected", event.target.value);
}
// cast to string
const search = current.toString();
// or const query = `${'?'.repeat(search.length && 1)}${search}`;
const query = search ? `?${search}` : "";
router.push(`${pathname}${query}`);
};
return (
<select value={selected} onChange={onSelect}>
<option value="">None</option>
{options.map((opt) => (
<option key={opt} value={opt}>
{opt}
</option>
))}
</select>
);
}; |
Beta Was this translation helpful? Give feedback.
-
@icyJoseph how could I do that avoiding page reloading? |
Beta Was this translation helpful? Give feedback.
-
I used shallow with my router when pushing.
|
Beta Was this translation helpful? Give feedback.
-
To update a searchParam but leave the others untouched I created a small helper function
Then to use it I did
|
Beta Was this translation helpful? Give feedback.
-
The validated answer works fine if you're updating params only at one place in your app and without multiple params updating at the same time which was not my case. So I built a hook using SWR which guarantees that updates are getting the latest state before updating url search params. For that, I'm using this hook that uses swr to share state across the app. Source for this hook : https://paco.me/writing/shared-hook-state-with-swr import useSWR from 'swr';
const useSharedState = <T>(key: string, initial: T) => {
const { data: state, mutate: setState } = useSWR<T>(key, {
fallbackData: initial
});
return [state, setState] as const;
}
export default useSharedState; I believe you can achieve the same result using React Context are any other global state management library. Then here is the hook I created : import { useEffect, useCallback, useMemo } from "react";
import { ReadonlyURLSearchParams, usePathname, useRouter, useSearchParams } from "next/navigation"
import useSharedState from "./useSharedState";
const useSharedSearchParams = (): [ReadonlyURLSearchParams, (key: string, value?: string) => void] => {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const initial = useMemo(() => (new URLSearchParams(searchParams.toString())), [])
const [params, setParams] = useSharedState(`${pathname}-search-params`, initial)
const updateSearchParam = useCallback(
(key: string, value?: string) => {
setParams((prev) => {
const previousParams = prev?.size ? prev.toString() : undefined
const newParams = new URLSearchParams(previousParams)
if (!value) {
newParams.delete(key)
} else {
newParams.set(key, value)
}
return newParams
})
},
[params]
)
useEffect(() => { // ensure that when component unmounts, shared state gets reset back to initial
return () => {
setParams(initial);
};
}, [initial]);
useEffect(() => {
router.push(pathname + '?' + params?.toString());
}, [params])
return [searchParams, updateSearchParam]
}
export default useSharedSearchParams which can then be used like this in any component : const AnyComponent = () => {
const [searchParams, updateSearchParams] = useSharedSearchParams();
const fooParam = searchParams.get('foo');
const barParam = searchParams.get('bar');
const deleteParam = (key: string) => updateSearchParams(key)
const updateParam = (key: string, value: string) => updateSearchParams(key, value)
const updateMultipleParam = () => {
updateSearchParams('foo', 'bar');
updateSearchParams('bar', 'baz'); // this update will get the latest search params state before updating
} // which is what we're looking for
return <div>AnyComponent</div>
} |
Beta Was this translation helpful? Give feedback.
-
This feel needlessly complex vs Next 12, where I could do...
|
Beta Was this translation helpful? Give feedback.
-
I am new to next 13, but is this valid so long as you are in a client component? const url = new URL(window.location.href);
url.searchParams.set("q", "farts and ass");
router.push(url.toString()); |
Beta Was this translation helpful? Give feedback.
-
Hello, I have next-translate working but in local. When i upload to vercel, does not work, only show the key of the common.json but do not translate. I am getting crazy why in local woprks and in production does not... please help! |
Beta Was this translation helpful? Give feedback.
-
Working with fetch is pity! I'm using the search params from the
But it is complaining about types!
How to solve this? |
Beta Was this translation helpful? Give feedback.
-
I think this is the best way for n.13 import { useRouter } from 'next/navigation';
router.push(`?tab=${key}` |
Beta Was this translation helpful? Give feedback.
-
This works fine for me: import { useRouter, useSearchParams } from "next/navigation";
const router = useRouter();
const searchParams = useSearchParams();
const updateQueryParam = (key: string, value: string) => {
const currentParams = new URLSearchParams(searchParams.toString());
currentParams.set(key, value);
router.replace(`?${currentParams.toString()}`, { scroll: false });
}; |
Beta Was this translation helpful? Give feedback.
-
I created a custom hook. And it works fine for me.Hook:export default function useQueryParams() {
const searchParams = useSearchParams();
const router = useRouter();
const pathname = usePathname();
const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams);
params.set(name, value);
return params.toString();
},
[searchParams],
);
const setQueryParam = (queryName: string, value: string) => {
router.push(`${pathname}?${createQueryString(queryName, value)}`);
};
return { queryParams: searchParams, createQueryString, setQueryParam };
} How to use:const { setQueryParam } = useQueryParams();
function handleChange(value) {
setQueryParam(paramName, value);
} |
Beta Was this translation helpful? Give feedback.
-
Hi everyone, This is how I am doing it on Next.js 14: const router = useRouter()
const searchParams = new URLSearchParams(useSearchParams());
searchParams.set(key, value)
router.push(`?${searchParams}`) If you want to update the path also: const router = useRouter()
const pathname = usePathname()
const searchParams = new URLSearchParams(useSearchParams());
searchParams.set(key, value)
router.push(`${pathname}?${searchParams}`) |
Beta Was this translation helpful? Give feedback.
-
The problem I had with: import { useRouter } from "next/navigation";
...
router.push('/pathname?param=value'); is that it triggers a full route rendering from the server side which I get that sometimes this might be what is desired but in my case I just wanted to open a modal and I wanted it to be purely client side with JS so I needed to create a context that is aware of next navigation and exposes a method that runs native browser I wish next 13/14 offered a way to simply do client side navigation if so desired through the router api or some other. |
Beta Was this translation helpful? Give feedback.
-
I used useState and the History API because My project hook'use client'
import { useSearchParams } from 'next/navigation'
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
const QueryParamNames = ['hostId', 'graphId'] as const
export type QueryStates = {
[key in (typeof QueryParamNames)[number]]?: string
}
export const useQueryStates = (): [
QueryStates,
Dispatch<SetStateAction<QueryStates>>,
] => {
const initParams = useSearchParams()
const [state, setState] = useState<QueryStates>(
QueryParamNames.map((key) =>
initParams?.get(key) ? { [key]: initParams.get(key) } : {}
).reduce((acc, cur) => ({ ...acc, ...cur }), {})
)
useEffect(() => {
const params = new URLSearchParams()
QueryParamNames.filter((key) => state[key]).forEach((key) =>
params.set(key, state[key]!)
)
const url = params.size ? `?${params.toString()}` : ''
window.history.replaceState(window.history.state, '', url)
}, [state])
return [state, setState]
} How to use'use client'
const [{ hostId, graphId }, setQuery] = useQueryStates()
/* if you need default values
useEffect(() => {
if (hostId || graphId) return
setQuery({
hostId: 'default-host-id',
graphId: 'default-graph-id',
})
}, [hostId, graphId, setQuery])
*/
// some code
<select
value={hostId}
onChange={(e) => setQuery({ hostId: e.target.value })}
>
<select
value={graphId}
onChange={(e) => setQuery({ hostId, graphId: e.target.value })}
> Tested
Versions
|
Beta Was this translation helpful? Give feedback.
-
using RSC and URLs is the most overly complicated and still way immature thing i had to deal with |
Beta Was this translation helpful? Give feedback.
-
if anyone is still wondering:
|
Beta Was this translation helpful? Give feedback.
-
The bigger issue for me is this: why are the URLSearchParams returned by What's even more confusing is that a In my opinion, the methods for setting and getting the same piece of state should be more aligned, but as it stands, this API expects you to use both the provided hook as well as the native |
Beta Was this translation helpful? Give feedback.
-
there's a great library to work with URLs in next called nuqs |
Beta Was this translation helpful? Give feedback.
-
I agree with most sentiments expressed in this thread. before I read this thread, I also created basically a wrapper with an interface similar to react routers |
Beta Was this translation helpful? Give feedback.
-
This works as it needs to in my use case |
Beta Was this translation helpful? Give feedback.
-
I implemented this hook: You can read the documentation here : |
Beta Was this translation helpful? Give feedback.
I think one way is to do this is to use the
useRouter
,usePathname
, anduseSearchParams
hooks, with theURLSearchParams
object API.