
Security News
AI Agent Lands PRs in Major OSS Projects, Targets Maintainers via Cold Outreach
An AI agent is merging PRs into major OSS projects and cold-emailing maintainers to drum up more work.
zustand-querystring
Advanced tools
Zustand middleware for URL query string sync.
npm install zustand-querystring
import { create } from 'zustand';
import { querystring } from 'zustand-querystring';
const useStore = create(
querystring(
(set) => ({
search: '',
page: 1,
setSearch: (search) => set({ search }),
setPage: (page) => set({ page }),
}),
{
select: () => ({ search: true, page: true }),
}
)
);
// URL: ?search=hello&page=2
querystring(storeCreator, {
select: undefined, // which fields to sync
key: false, // false | 'state'
prefix: '', // prefix for URL params
format: marked, // serialization format
syncNull: false, // sync null values
syncUndefined: false, // sync undefined values
url: undefined, // request URL for SSR
})
selectControls which state fields sync to URL. Receives pathname, returns object with true for fields to sync.
// All fields
select: () => ({ search: true, page: true, filters: true })
// Route-based
select: (pathname) => ({
search: true,
filters: pathname.startsWith('/products'),
adminSettings: pathname.startsWith('/admin'),
})
// Nested fields
select: () => ({
user: {
name: true,
settings: { theme: true },
},
})
keyfalse (default): Each field becomes a separate URL param
?search=hello&page=2&filters.sort=name
'state' (or any string): All state in one param
?state=search%3Dhello%2Cpage%3A2
prefixAdds prefix to all params. Use when multiple stores share URL.
querystring(storeA, { prefix: 'a_', select: () => ({ search: true }) })
querystring(storeB, { prefix: 'b_', select: () => ({ filter: true }) })
// URL: ?a_search=hello&b_filter=active
syncNull / syncUndefinedBy default, null and undefined reset to initial state (removed from URL). Set to true to write them.
urlFor SSR, pass the request URL:
querystring(store, { url: request.url, select: () => ({ search: true }) })
replaceState)Only values different from initial state are written to URL:
// Initial: { search: '', page: 1, sort: 'date' }
// Current: { search: 'hello', page: 1, sort: 'name' }
// URL: ?search=hello&sort=name
// (page omitted - matches initial)
Type handling:
Three built-in formats:
| Format | Example Output |
|---|---|
marked | count:5,tags@a,b~ |
plain | count=5&tags=a,b |
json | count=5&tags=%5B%22a%22%5D |
import { marked } from 'zustand-querystring/format/marked';
import { plain } from 'zustand-querystring/format/plain';
import { json } from 'zustand-querystring/format/json';
querystring(store, { format: plain })
Type markers: : primitive, = string, @ array, . object
Delimiters: , separator, ~ terminator, _ escape
import { createFormat } from 'zustand-querystring/format/marked';
const format = createFormat({
typeObject: '.',
typeArray: '@',
typeString: '=',
typePrimitive: ':',
separator: ',',
terminator: '~',
escapeChar: '_',
datePrefix: 'D',
});
Dot notation for nesting, comma-separated arrays.
import { createFormat } from 'zustand-querystring/format/plain';
const format = createFormat({
entrySeparator: ',', // between entries in namespaced mode
nestingSeparator: '.', // for nested keys
arraySeparator: ',', // or 'repeat' for ?tags=a&tags=b&tags=c
escapeChar: '_',
nullString: 'null',
undefinedString: 'undefined',
infinityString: 'Infinity', // string representation of Infinity
negativeInfinityString: '-Infinity',
nanString: 'NaN',
});
URL-encoded JSON. No configuration.
Implement QueryStringFormat:
import type { QueryStringFormat, QueryStringParams, ParseContext } from 'zustand-querystring';
const myFormat: QueryStringFormat = {
// For key: 'state' (namespaced mode)
stringify(state: object): string {
return encodeURIComponent(JSON.stringify(state));
},
parse(value: string, ctx?: ParseContext): object {
return JSON.parse(decodeURIComponent(value));
},
// For key: false (standalone mode)
stringifyStandalone(state: object): QueryStringParams {
const result: QueryStringParams = {};
for (const [key, value] of Object.entries(state)) {
result[key] = [encodeURIComponent(JSON.stringify(value))];
}
return result;
},
parseStandalone(params: QueryStringParams, ctx: ParseContext): object {
const result: Record<string, unknown> = {};
for (const [key, values] of Object.entries(params)) {
result[key] = JSON.parse(decodeURIComponent(values[0]));
}
return result;
},
};
querystring(store, { format: myFormat })
Types:
QueryStringParams = Record<string, string[]> (values always arrays)ctx.initialState available for type coercionconst useStore = create(
querystring(
(set) => ({
query: '',
page: 1,
setQuery: (query) => set({ query, page: 1 }), // reset page on new query
setPage: (page) => set({ page }),
}),
{ select: () => ({ query: true, page: true }) }
)
);
const useFilters = create(
querystring(filtersStore, {
prefix: 'f_',
select: () => ({ category: true, price: true }),
})
);
const usePagination = create(
querystring(paginationStore, {
prefix: 'p_',
select: () => ({ page: true, limit: true }),
})
);
// URL: ?f_category=shoes&f_price=100&p_page=2&p_limit=20
// app/page.tsx
export default async function Page({ searchParams }) {
// Store reads from URL on init
}
// Middleware
import { querystring } from 'zustand-querystring';
// Formats
import { marked, createFormat } from 'zustand-querystring/format/marked';
import { plain, createFormat } from 'zustand-querystring/format/plain';
import { json } from 'zustand-querystring/format/json';
// Types
import type {
QueryStringOptions,
QueryStringFormat,
QueryStringParams,
ParseContext,
} from 'zustand-querystring';
FAQs
Zustand middleware for URL query string sync.
The npm package zustand-querystring receives a total of 1,312 weekly downloads. As such, zustand-querystring popularity was classified as popular.
We found that zustand-querystring demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
An AI agent is merging PRs into major OSS projects and cold-emailing maintainers to drum up more work.

Research
/Security News
Chrome extension CL Suite by @CLMasters neutralizes 2FA for Facebook and Meta Business accounts while exfiltrating Business Manager contact and analytics data.

Security News
After Matplotlib rejected an AI-written PR, the agent fired back with a blog post, igniting debate over AI contributions and maintainer burden.