npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@goranton/vue-url-state

v0.1.0

Published

Type-safe URL query state management for Vue and Nuxt

Readme

vue-url-state

Type-safe URL query state management for Vue and Nuxt.

vue-url-state helps manage filters, pagination, tabs, and other page state through typed URL query params.

Why

Working with query params directly usually creates the same issues across pages:

  • query params are string-based by default
  • filter state gets duplicated between URL state and component state
  • router.replace/router.push logic is repeated everywhere
  • apply-mode filters require extra local buffering code
  • default values and URL cleanup are easy to get wrong

Install

npm install @goranton/vue-url-state
pnpm add @goranton/vue-url-state
yarn add @goranton/vue-url-state

Install from npm using the scoped package name.

Basic Schema

import {
  arrayParam,
  booleanParam,
  defineQuerySchema,
  enumParam,
  numberParam,
  stringParam,
} from '@goranton/vue-url-state';

const usersQuerySchema = defineQuerySchema({
  search: stringParam(),
  page: numberParam({ defaultValue: 1 }),
  status: enumParam(['active', 'blocked', 'pending'] as const),
  tags: arrayParam(stringParam()),
  onlyWithErrors: booleanParam(),
});

Inferred state type:

{
  search: string | undefined;
  page: number;
  status: 'active' | 'blocked' | 'pending' | undefined;
  tags: string[] | undefined;
  onlyWithErrors: boolean | undefined;
}

useQueryState

import { useQueryState } from '@goranton/vue-url-state';

const query = useQueryState(usersQuerySchema);

query.state.value;
await query.patch({ search: 'anton', page: 1 });
await query.remove(['status']);
await query.reset();
  • state is computed from route.query
  • patch() updates URL query values
  • default history mode is replace
  • unknown query params are preserved by default
  • default values are cleaned from URL by default

useQueryField

import { useQueryField, useQueryState } from '@goranton/vue-url-state';

const query = useQueryState(usersQuerySchema);

const search = useQueryField(query, 'search', {
  resetOnChange: {
    page: 1,
  },
});

search.value = 'anton';
  • useQueryField returns a writable computed ref for a single field.
  • It is useful for v-model bindings in forms.
  • Setting the field updates URL state via query.patch().
  • resetOnChange can reset related fields (for example page: 1 when search changes).
  • onError can be used to observe fire-and-forget helper failures.
  • useQueryField is fire-and-forget because writable computed setters cannot be awaited directly.
  • For awaited navigation, explicit error handling, or fully controlled async flows, use query.patch() directly.

useDebouncedQueryField

import { useDebouncedQueryField, useQueryState } from '@goranton/vue-url-state';

const query = useQueryState(usersQuerySchema);

const search = useDebouncedQueryField(query, 'search', {
  debounce: 300,
  resetOnChange: {
    page: 1,
  },
});

search.value = 'anton';
  • useDebouncedQueryField returns a local Ref.
  • It is useful for search inputs and similar frequently changing fields.
  • The local value updates immediately.
  • URL updates happen only after the debounce delay.
  • resetOnChange can reset related fields (for example page: 1).
  • onError can be used to observe debounced fire-and-forget helper failures.
  • Debounce is intentionally not part of useQueryState().
  • For immediate updates, use useQueryField().
  • For explicit Apply-button flows, use useQueryBuffer().
  • useDebouncedQueryField is also fire-and-forget because refs are not awaitable setters.

useQueryBuffer

import { useQueryBuffer, useQueryState } from '@goranton/vue-url-state';

const query = useQueryState(usersQuerySchema);
const buffer = useQueryBuffer(query);

buffer.patch({ search: 'anton' });

if (buffer.isDirty.value) {
  await buffer.apply();
}
  • draft changes stay local and do not update URL immediately
  • apply() writes the draft to URL through query.patch()
  • reset() discards local draft edits
  • clear() resets draft to default/empty state
  • apply() is awaitable and is the recommended controlled async flow.

Pure Helpers

import { deserializeQuery, serializeQuery } from '@goranton/vue-url-state';

const state = deserializeQuery(usersQuerySchema, {
  page: '2',
  status: 'active',
});

const raw = serializeQuery(usersQuerySchema, {
  search: 'anton',
  page: 1,
});

API

  • Schema: defineQuerySchema
  • Params: stringParam, numberParam, booleanParam, enumParam, arrayParam
  • Pure helpers: deserializeQuery, serializeQuery, patchQuery, removeQueryKeys, resetQuery
  • Vue composables: useQueryState, useQueryField, useDebouncedQueryField, useQueryBuffer

Defaults

  • Params without defaultValue infer as T | undefined.
  • Params with defaultValue infer as T.
  • cleanDefaults removes default values from URL by default.
  • preserveUnknown keeps unknown query params by default.

Behavior Notes

  • patchQuery() and query.patch() normalize full schema-owned query state, not only touched keys.
  • This keeps URLs canonical: invalid schema values are removed or defaulted, and default values are cleaned by default.
  • Unknown query params are preserved by default.
  • For non-array params receiving repeated query keys, the first value is used.
  • arrayParam() uses repeated query keys (for example ?tags=vue&tags=nuxt).
  • Nested array params are not supported/documented.

Status

  • Early-stage project.
  • Built for Vue 3 + Vue Router 4.
  • Nuxt should work through Vue Router integration, but there is no Nuxt-specific module yet.
  • API may still change before 1.0.