Skip to content

iamsomraj/vue-qs

Repository files navigation

vue-qs

vue-qs social

CI npm version license

Note: This library is currently in active development. APIs may change significantly between versions. Please use with caution and expect breaking changes.

📖 Documentation: https://iamsomraj.github.io/vue-qs/
🌏 中文文档: https://iamsomraj.github.io/vue-qs/zh/

Type‑safe, reactive URL query parameters for Vue 3. Inspired by nuqs (React) but built for the Vue Composition API.

✨ Features

  • 🔄 Bidirectional Sync: URL parameters stay in sync with your reactive state
  • 🎯 Type Safety: Full TypeScript support with type inference
  • 🚀 Vue 3 Ready: Built for Vue 3 Composition API
  • 🔧 Flexible: Works with or without Vue Router
  • 🛡️ SSR Safe: Server-side rendering compatible
  • 📦 Tree Shakeable: Only import what you need
  • 🎨 Customizable: Built-in codecs + custom serialization support

🎯 Why vue-qs?

Keep UI state (page, filters, search text, sort, tabs) in the URL so users can:

  • 🔄 Refresh and keep state
  • 🔗 Share links with specific state
  • ⬅️➡️ Use browser back/forward buttons

vue-qs gives you composables that feel like normal refs/reactive objects, but they automatically stay in sync with the URL query string.

📦 Installation

npm install vue-qs
# or
pnpm add vue-qs
# or
bun add vue-qs

Peer Dependencies:

  • vue ^3.3.0 (required)
  • vue-router ^4.2.0 (optional, for router integration)

🚀 Quick Start

Basic Usage (No Router)

<script setup lang="ts">
import { queryRef } from 'vue-qs';

// Create a ref bound to ?name=...
// Falls back to default value if param is missing
const name = queryRef('name', {
  defaultValue: '',
});
</script>

<template>
  <input v-model="name" placeholder="Your name" />
</template>

Multiple Parameters

<script setup lang="ts">
import { queryReactive } from 'vue-qs';

// Each field config controls parsing, defaults, and omission rules
const queryState = queryReactive({
  q: { defaultValue: '' },
  page: { defaultValue: 1, codec: numberCodec },
  showDetails: { defaultValue: false, codec: booleanCodec },
});
</script>

<template>
  <input v-model="queryState.q" placeholder="Search..." />
  <button @click="queryState.page++">Next Page</button>
  <button @click="queryState.showDetails = !queryState.showDetails">Toggle Details</button>
</template>

🔗 Vue Router Integration

Option 1: Global Plugin (Recommended)

// main.ts
import { createApp } from 'vue';
import { createVueQsPlugin, createVueRouterAdapter } from 'vue-qs';
import { router } from './router';
import App from './App.vue';

const app = createApp(App);

app.use(
  createVueQsPlugin({
    queryAdapter: createVueRouterAdapter(router),
  })
);
app.use(router);
app.mount('#app');

Option 2: Per-Component Adapter

<script setup lang="ts">
import { queryRef, createVueRouterAdapter } from 'vue-qs';
import { useRouter } from 'vue-router';

const router = useRouter();
const adapter = createVueRouterAdapter(router);

const page = queryRef('page', {
  defaultValue: 1,
  codec: numberCodec,
  queryAdapter: adapter,
});
</script>

🔧 Codecs (Type Conversion)

Import ready‑made codecs for common types:

import {
  stringCodec,
  numberCodec,
  booleanCodec,
  dateISOCodec,
  createArrayCodec,
  createJsonCodec,
} from 'vue-qs';

// Basic types
const name = queryRef('name', {
  defaultValue: '',
  codec: stringCodec,
});

const page = queryRef('page', {
  defaultValue: 1,
  codec: numberCodec,
});

const isActive = queryRef('active', {
  defaultValue: false,
  codec: booleanCodec,
});

// Complex types
const tags = queryRef('tags', {
  defaultValue: [] as string[],
  codec: createArrayCodec(stringCodec),
});

const filters = queryRef('filters', {
  defaultValue: { category: 'all', sort: 'name' },
  codec: createJsonCodec<{ category: string; sort: string }>(),
});

⚙️ Configuration Options

Shared Options

Option Type Default Description
defaultValue T - Initial value if parameter is missing
codec QueryCodec<T> stringCodec Parser and serializer for the type
parse QueryParser<T> - Custom parser function (overrides codec)
serializeFunction QuerySerializer<T> - Custom serializer function (overrides codec)
shouldOmitDefault boolean true Remove from URL when equal to default
isEqual (a: T, b: T) => boolean Object.is Custom equality function
historyStrategy 'replace' | 'push' 'replace' Browser history update strategy
queryAdapter QueryAdapter - Override default query adapter

Custom Equality Example

const filters = queryRef('filters', {
  defaultValue: { category: 'all', sort: 'name' },
  codec: createJsonCodec<{ category: string; sort: string }>(),
  isEqual: (a, b) => a.category === b.category && a.sort === b.sort,
});

🛡️ SSR Safety

vue-qs is SSR-safe. On the server, the composables use an internal cache until hydration, so you can render initial HTML safely without touching window.

📚 API Reference

queryRef(name, options)

Creates a reactive ref that syncs with a URL query parameter.

function queryRef<T>(parameterName: string, options?: QueryRefOptions<T>): Ref<T>;

queryReactive(schema, options)

Creates a reactive object that syncs multiple URL query parameters.

function queryReactive<TSchema extends QueryParameterSchema>(
  parameterSchema: TSchema,
  options?: QueryReactiveOptions
): ReactiveQueryState<TSchema>;

createHistoryAdapter()

Creates an adapter for browser History API (default).

function createHistoryAdapter(): QueryAdapter;

createVueRouterAdapter(router)

Creates an adapter for Vue Router integration.

function createVueRouterAdapter(router: Router): QueryAdapter;

Development Setup

# Clone and install
git clone https://github.com/iamsomraj/vue-qs.git
cd vue-qs
bun install

# Development
bun run dev          # Watch mode
bun run test         # Run tests
bun run typecheck    # Type checking
bun run lint         # Linting
bun run docs:dev     # Documentation dev server

📄 License

MIT License - see LICENSE for details.

🙏 Acknowledgments

  • Inspired by nuqs for React
  • Built with Vue 3 Composition API
  • TypeScript support powered by TypeScript