Skip to content

Instantly share code, notes, and snippets.

@tarasyarema
Created September 24, 2025 12:42
Show Gist options
  • Save tarasyarema/b0f54777a4006a820d85dd9d0b42bd95 to your computer and use it in GitHub Desktop.
Save tarasyarema/b0f54777a4006a820d85dd9d0b42bd95 to your computer and use it in GitHub Desktop.
React - useTypedQueryParams
import { useCallback, useMemo } from "react";
import { useSearchParams } from "react-router";
export type QueryParamsMap = Record<string, string | undefined>;
export interface UseQueryParamsReturn {
/**
* All query parameters as a key-value map
*/
params: QueryParamsMap;
/**
* Get a specific query parameter value
*/
getParam: (key: string) => string | undefined;
/**
* Set a single query parameter
*/
setParam: (key: string, value: string | undefined) => void;
/**
* Set multiple query parameters at once
*/
setParams: (updates: QueryParamsMap) => void;
/**
* Remove a query parameter
*/
removeParam: (key: string) => void;
/**
* Remove multiple query parameters
*/
removeParams: (keys: string[]) => void;
/**
* Clear all query parameters
*/
clearParams: () => void;
/**
* Check if a query parameter exists
*/
hasParam: (key: string) => boolean;
}
/**
* Hook for managing URL query parameters
*
* @example
* ```tsx
* function MyComponent() {
* const { params, setParam, getParam } = useQueryParams();
*
* const currentTab = getParam('tab') ?? 'details';
*
* const handleTabChange = (tab: string) => {
* setParam('tab', tab);
* };
*
* return (
* <div>
* <p>Current params: {JSON.stringify(params)}</p>
* <button onClick={() => handleTabChange('issues')}>
* Switch to Issues
* </button>
* </div>
* );
* }
* ```
*/
export function useQueryParams(): UseQueryParamsReturn {
const [searchParams, setSearchParams] = useSearchParams();
// Convert URLSearchParams to a plain object
const params = useMemo<QueryParamsMap>(() => {
const result: QueryParamsMap = {};
searchParams.forEach((value, key) => {
result[key] = value;
});
return result;
}, [searchParams]);
const getParam = useCallback(
(key: string): string | undefined => {
return searchParams.get(key) ?? undefined;
},
[searchParams],
);
const setParam = useCallback(
(key: string, value: string | undefined) => {
setSearchParams((prev) => {
const newParams = new URLSearchParams(prev);
if (value === undefined || value === null || value === "") {
newParams.delete(key);
} else {
newParams.set(key, value);
}
return newParams;
});
},
[setSearchParams],
);
const setParams = useCallback(
(updates: QueryParamsMap) => {
setSearchParams((prev) => {
const newParams = new URLSearchParams(prev);
Object.entries(updates).forEach(([key, value]) => {
if (value === undefined || value === null || value === "") {
newParams.delete(key);
} else {
newParams.set(key, value);
}
});
return newParams;
});
},
[setSearchParams],
);
const removeParam = useCallback(
(key: string) => {
setSearchParams((prev) => {
const newParams = new URLSearchParams(prev);
newParams.delete(key);
return newParams;
});
},
[setSearchParams],
);
const removeParams = useCallback(
(keys: string[]) => {
setSearchParams((prev) => {
const newParams = new URLSearchParams(prev);
keys.forEach((key) => newParams.delete(key));
return newParams;
});
},
[setSearchParams],
);
const clearParams = useCallback(() => {
setSearchParams({});
}, [setSearchParams]);
const hasParam = useCallback(
(key: string): boolean => {
return searchParams.has(key);
},
[searchParams],
);
return {
params,
getParam,
setParam,
setParams,
removeParam,
removeParams,
clearParams,
hasParam,
};
}
/**
* Typed version of useQueryParams for better type safety
*
* @example
* ```tsx
* interface MyParams {
* tab?: 'details' | 'issues';
* page?: string;
* filter?: string;
* }
*
* function MyComponent() {
* const { params, setParam } = useTypedQueryParams<MyParams>();
*
* const currentTab = params.tab ?? 'details';
*
* return (
* <div>
* <button onClick={() => setParam('tab', 'issues')}>
* Issues Tab
* </button>
* </div>
* );
* }
* ```
*/
export function useTypedQueryParams<T = QueryParamsMap>(): Omit<
UseQueryParamsReturn,
"params"
> & { params: T } {
const queryParams = useQueryParams();
return {
...queryParams,
params: queryParams.params as T,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment