Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spicy-carrots-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-query-params": patch
---

Add support for multi-value params
51 changes: 48 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Svelte Query Params

The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built on Svelte 5 [runes](https://svelte-5-preview.vercel.app/docs/runes) and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built for Svelte 5 and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.

## Installation

Since Svelte Query Params uses runes, [`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
[`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:

```bash
npm install svelte-query-params svelte@next
Expand All @@ -26,7 +26,7 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz

## Features

- **Reactivity**: The library leverages Svelte's new runes reactivity system, providing a reactive object that reflects the current state of query parameters.
- **Reactivity**: The library providies a reactive object that reflects the current state of query parameters.

- **Browser and Server Support**: The utility is designed to work seamlessly in both browser and server environments.

Expand All @@ -36,6 +36,10 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz

- **Event Handling**: Automatically handles `popstate` events for accurate synchronisation with browser history.

- **Serialisation**: Control how query params are serialised into strings to the browser

- **Multi-value params**: Supports multi-value query parameters with ease

## Usage

In some lib file e.g., `src/lib/params.ts`:
Expand Down Expand Up @@ -141,6 +145,47 @@ const useQueryParams = createUseQueryParams({
});
```

### Array Values

With a function validator, you may receive the param as either a string, an array of strings, or undefined. As a result, you must handle all three cases to support multi-value params:

```javascript
const validators = {
categories: (value) => {
if (!value) return []
return Array.isArray(value) ? value : [value]
}
}
```

With Zod, you need to handle the case where there's either 0 or 1 query param value as this library will not infer this as an array beforehand. You must define your array parameter like:

```javascript
import { z } from "zod";

z.object({
categories: z
.union([z.string().array(), z.string()])
.default([])
.transform((c) => (Array.isArray(c) ? c : [c])),
})
```

The union between a string and array of strings handles 1 or more query params; a default is set to the empty array to allow the parameter to be omitted from the URL and it's transformed at the end to convert the single value param into an array.

In the same manner, with Valibot:

```javascript
import * as v from "valibot";

v.object({
categories: v.pipe(
v.optional(v.union([v.array(v.string()), v.string()]), []),
v.transform((c) => (Array.isArray(c) ? c : [c]))
),
});
```

## Options

`createUseQueryParams` takes an options object as the second argument, with the following properties:
Expand Down
50 changes: 24 additions & 26 deletions examples/sveltekit/src/routes/multiselect/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,35 @@
import { page } from "$app/stores";
import { useMultiSelectFilters } from "$lib/hooks/multi-select";

const [params, helpers] = useMultiSelectFilters($page.url);
const [q, helpers] = useMultiSelectFilters($page.url);
const CATEGORIES = ["books", "electronics", "toys"];

function updateCategories(category: string) {
const categories = params.categories.includes(category)
? params.categories.filter((c) => c !== category)
: [...params.categories, category];
const categories = q.categories.includes(category)
? q.categories.filter((c) => c !== category)
: [...q.categories, category];
helpers.update({ categories });
}
</script>

<ul>
<li>
<label>
<input
type="checkbox"
value="books"
onchange={() => updateCategories("books")}
checked={params.categories.includes("books")}
/>
Books
</label>
</li>
<li>
<label>
<input
type="checkbox"
value="electronics"
onchange={() => updateCategories("electronics")}
checked={params.categories.includes("electronics")}
/>
Electronics
</label>
</li>
{#each CATEGORIES as category}
<li>
<label>
<input
type="checkbox"
value={category}
onchange={() => updateCategories(category)}
checked={q.categories.includes(category)}
/>
{category}
</label>
</li>
{/each}
</ul>

<ul>
{#each q.categories as category}
<li>{category}</li>
{/each}
</ul>
51 changes: 48 additions & 3 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Svelte Query Params

The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built on Svelte 5 [runes](https://svelte-5-preview.vercel.app/docs/runes) and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built for Svelte 5 and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.

## Installation

Since Svelte Query Params uses runes, [`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
[`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:

```bash
npm install svelte-query-params svelte@next
Expand All @@ -26,7 +26,7 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz

## Features

- **Reactivity**: The library leverages Svelte's new runes reactivity system, providing a reactive object that reflects the current state of query parameters.
- **Reactivity**: The library providies a reactive object that reflects the current state of query parameters.

- **Browser and Server Support**: The utility is designed to work seamlessly in both browser and server environments.

Expand All @@ -36,6 +36,10 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz

- **Event Handling**: Automatically handles `popstate` events for accurate synchronisation with browser history.

- **Serialisation**: Control how query params are serialised into strings to the browser

- **Multi-value params**: Supports multi-value query parameters with ease

## Usage

In some lib file e.g., `src/lib/params.ts`:
Expand Down Expand Up @@ -141,6 +145,47 @@ const useQueryParams = createUseQueryParams({
});
```

### Array Values

With a function validator, you may receive the param as either a string, an array of strings, or undefined. As a result, you must handle all three cases to support multi-value params:

```javascript
const validators = {
categories: (value) => {
if (!value) return []
return Array.isArray(value) ? value : [value]
}
}
```

With Zod, you need to handle the case where there's either 0 or 1 query param value as this library will not infer this as an array beforehand. You must define your array parameter like:

```javascript
import { z } from "zod";

z.object({
categories: z
.union([z.string().array(), z.string()])
.default([])
.transform((c) => (Array.isArray(c) ? c : [c])),
})
```

The union between a string and array of strings handles 1 or more query params; a default is set to the empty array to allow the parameter to be omitted from the URL and it's transformed at the end to convert the single value param into an array.

In the same manner, with Valibot:

```javascript
import * as v from "valibot";

v.object({
categories: v.pipe(
v.optional(v.union([v.array(v.string()), v.string()]), []),
v.transform((c) => (Array.isArray(c) ? c : [c]))
),
});
```

## Options

`createUseQueryParams` takes an options object as the second argument, with the following properties:
Expand Down
12 changes: 7 additions & 5 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@
},
"keywords": [
"reactive",
"search",
"search-params",
"search params",
"search-parameters",
"search parameters",
"query",
"querystring",
"query parameters",
"query-parameters",
"query params",
"query-params",
"svelte",
"sveltejs",
"sveltekit",
"runes"
"ssr",
"browser",
"url"
],
"sideEffects": false,
"publishConfig": {
Expand Down Expand Up @@ -83,10 +88,7 @@
"module": "dist/index.svelte.js",
"svelte": "dist/index.svelte.js",
"types": "dist/index.svelte.d.ts",
"files": [
"dist",
"README.md"
],
"files": ["dist", "README.md"],
"engines": {
"node": ">=v20.0.0"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ function playwrightDir(partialPath: string) {
const CI = !!process.env.CI;

export default defineConfig({
expect: {
timeout: CI ? 10000 : 2000,
},
testDir: playwrightDir("specs"),
outputDir: playwrightDir("results"),
webServer: {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/lib/__test__/params.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ describe("Type tests", () => {
{
name: "function validators",
schema: {
id: (value: string | undefined) => Number(value),
q: (value: string | undefined) => value,
id: (value: string | string[] | undefined) => Number(value),
q: (value: string | string[] | undefined) => value,
},
},
{
name: "mixes and matched",
name: "mix and match",
schema: {
id: z.number(),
q: (value: string | undefined) => value,
q: (value: string | string[] | undefined) => value,
},
},
];
Expand Down
Loading