T
TanStack•5mo ago
rare-sapphire

Anyone using Nuqs with Tanstack Start?

In my existing codebase (nextjs) I am using Nuqs all over the place, but Im running into an issue with the existing adapter for react. Is anyone able to use Nuqs with Tanstack or is it an anti-pattern with Tanstack Router? So far I'm used to the syntax of Nuqs and its ease of use with useState.
13 Replies
harsh-harlequin
harsh-harlequin•5mo ago
AFAIK there is no nuqs adapter for TSS/TSR. We have previously used nuqs but Tanstack router just providers much better type safety than nuqs ever could. I think the nuqs github issues has a closed ticket/discussion (saw that i while back) where they also agreed that there is no reason to use nuqs if you have Tanstack router 🙂 But i do understand that you like the simple API of nuqs. I copy-pasted and adapted a simple hook to smooth migration:
import { RegisteredRouter, useNavigate, useSearch } from "@tanstack/react-router";
import { FullSearchSchema } from "@tanstack/router-core";
import { useCallback } from "react";

export type FullSearchParams = FullSearchSchema<RegisteredRouter["routeTree"]>;
export type SearchKeys = keyof FullSearchParams;

/**
* Allows reading/updating a single search param easily
* @returns
*/
export function useSearchParam<K extends SearchKeys>(searchParam: K, replaceHistory = true) {
const searchValue = useSearch({
strict: false,
select: (search: FullSearchParams) => search[searchParam as SearchKeys],
}) as FullSearchParams[K];

const navigate = useNavigate();

const setSearchParam = useCallback(
(newValue: FullSearchParams[K]) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
[searchParam]: newValue,
}),
replace: replaceHistory,
});
},
[navigate, searchParam, replaceHistory]
);

return [searchValue, setSearchParam] as const;
}
import { RegisteredRouter, useNavigate, useSearch } from "@tanstack/react-router";
import { FullSearchSchema } from "@tanstack/router-core";
import { useCallback } from "react";

export type FullSearchParams = FullSearchSchema<RegisteredRouter["routeTree"]>;
export type SearchKeys = keyof FullSearchParams;

/**
* Allows reading/updating a single search param easily
* @returns
*/
export function useSearchParam<K extends SearchKeys>(searchParam: K, replaceHistory = true) {
const searchValue = useSearch({
strict: false,
select: (search: FullSearchParams) => search[searchParam as SearchKeys],
}) as FullSearchParams[K];

const navigate = useNavigate();

const setSearchParam = useCallback(
(newValue: FullSearchParams[K]) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
[searchParam]: newValue,
}),
replace: replaceHistory,
});
},
[navigate, searchParam, replaceHistory]
);

return [searchValue, setSearchParam] as const;
}
rare-sapphire
rare-sapphireOP•5mo ago
Thanks! I'll try the hook tomorrow morning 🙂
rare-sapphire
rare-sapphireOP•5mo ago
hmm running into some typescript issues..
No description
rare-sapphire
rare-sapphireOP•5mo ago
do you have an example of how to unset the searchParam?
xenial-black
xenial-black•5mo ago
There’s actually a pr for a tanstack adapter I saw it a few days ago. I am trying to use the data table @sadmann7 published and it uses nuqs
xenial-black
xenial-black•5mo ago
Hey y’all, I’m the author of nuqs. There is indeed a PR for an adapter for TanStack Router (Start’s SSR capabilities might come later in another PR), which makes the useQueryState(s) hooks work, but it’s only the tip of the iceberg in getting a good integration with TSR/TSS. Before going further, let’s address the “why”. I'd like to revisit my comment in the discussion mentioned above: I think there are two reasons where nuqs support in TSR makes sense: - Facilitating migrations between frameworks (eg: from Next.js, React Router, Remix etc) - Using shared, framework-agnostic components (like @DiamondDragon's case) Now, getting a good integration involves type-safe routing. I’ve been working on adding StandardSchema support for nuqs’ search params definitions this weekend, which could provide inputs for validateSearch, but there are still differences in which search params are handled between the two libraries that make a 1:1 adaptation non-trivial. For example: - TSR has a single place where search params encoding/decoding is done (eg: JSON, or base64, or other schemes, but pick a single one), whereas nuqs defines this per-key. - nuqs has a urlKeys feature that allow remapping keys in the URL to shorter names while keeping business-logic related variable names in your codebase. While we could take this into account when connecting nuqs definitions to TSR, this would “leak” (as in abstraction leak) the shortnames into the type-safe routing parts (Link component & router calls).
harsh-harlequin
harsh-harlequin•5mo ago
Strange. what are your errors exactly? i did not have the "type" imports but even when swapping to those it still works on my side. Do you use the latest packages from TSR? I have not tried unsetting right now but shouldn't passing undefined work?
rare-sapphire
rare-sapphireOP•5mo ago
"@tanstack/react-router": "^1.115.2",
"@tanstack/router-core": "^1.115.0",
"@tanstack/react-router": "^1.115.2",
"@tanstack/router-core": "^1.115.0",
just updated to the latest, still type issues
harsh-harlequin
harsh-harlequin•5mo ago
What exactly is the issue that you see? seems like 2 different ones. one o the FullSearchParams type and one in the const searchValue
rare-sapphire
rare-sapphireOP•5mo ago
okay nvm, I think the typescript server was lagging. The issues were not directly resolved after rm -rf node modules and pnpm i, but it did work after reopening the window Thanks for the help
harsh-harlequin
harsh-harlequin•5mo ago
wonderful 🙂 A note of caution i am currently stuck on this hook as i use it like i did with nuqs to save whatever the user currently inputs into a textfield. because TSR requires navigate to update a search param a ton of other methods (like beforeLoad and loaders) run again, causing the UI to become really laggy. So use with caution 🙂
xenial-black
xenial-black•5mo ago
sounds like a debounce is necessary btw loaders are not re-run if the search param you change is not a loaderDep
harsh-harlequin
harsh-harlequin•5mo ago
oh ok then only my beforeLoad and the re-renders are the issue i assume. I will try a debounce next but seems like a workaround 🙂

Did you find this page helpful?