Skip to content

Persistence

Filter rules can be saved to localStorage (or any storage) and restored later, so users don’t lose their filters on page reload.

A FilterGroup is a plain object, so JSON.stringify works for most cases. However, if your schema includes Date fields, you need custom handling since JSON.stringify converts dates to strings.

import type { FilterGroup } from "@fn-sphere/filter";
export function serializeFilterGroup(filterGroup: FilterGroup): string {
const replacer = function (this: any, key: string) {
return this[key] instanceof Date
? { __type: "Date", value: this[key].toISOString() }
: this[key];
};
return JSON.stringify(filterGroup, replacer);
}

When reading back, revive Date objects and validate the structure before using it.

import type { FilterGroup } from "@fn-sphere/filter";
export function deserializeFilterGroup(serialized: string): FilterGroup {
const deserialized = JSON.parse(serialized, (_, value) => {
if (value && typeof value === "object" && value.__type === "Date") {
return new Date(value.value);
}
return value;
});
return deserialized as FilterGroup;
}

Use the onRuleChange callback to save the filter rule whenever it changes.

import {
FilterBuilder,
FilterSphereProvider,
useFilterSphere,
} from "@fn-sphere/filter";
const STORAGE_KEY = "my-filter-rule";
function MyFilter({ schema }) {
const { context } = useFilterSphere({
schema,
defaultRule: loadFilterRule(),
onRuleChange: ({ filterRule }) => {
localStorage.setItem(STORAGE_KEY, serializeFilterGroup(filterRule));
},
});
return (
<FilterSphereProvider context={context}>
<FilterBuilder />
</FilterSphereProvider>
);
}

Read from storage on mount and pass it as defaultRule. Wrap it in a try-catch so corrupted data doesn’t break the UI.

import { createFilterGroup, createSingleFilter } from "@fn-sphere/filter";
const fallbackRule = createFilterGroup({
op: "and",
conditions: [createSingleFilter()],
});
function loadFilterRule() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (!saved) return fallbackRule;
return deserializeFilterGroup(saved);
} catch {
return fallbackRule;
}
}

Putting it all together:

import {
FilterBuilder,
FilterSphereProvider,
useFilterSphere,
createFilterGroup,
createSingleFilter,
type FilterGroup,
} from "@fn-sphere/filter";
import { z } from "zod";
const STORAGE_KEY = "my-filter-rule";
const schema = z.object({
name: z.string().describe("Name"),
createdAt: z.date().describe("Created At"),
});
const fallbackRule = createFilterGroup({
op: "and",
conditions: [createSingleFilter()],
});
// --- Serialization ---
function serializeFilterGroup(filterGroup: FilterGroup): string {
const replacer = function (this: any, key: string) {
return this[key] instanceof Date
? { __type: "Date", value: this[key].toISOString() }
: this[key];
};
return JSON.stringify(filterGroup, replacer);
}
function deserializeFilterGroup(serialized: string): FilterGroup {
const deserialized = JSON.parse(serialized, (_, value) => {
if (value && typeof value === "object" && value.__type === "Date") {
return new Date(value.value);
}
return value;
});
if (
!deserialized ||
deserialized.type !== "FilterGroup" ||
!Array.isArray(deserialized.conditions)
) {
throw new Error("Invalid FilterGroup structure");
}
return deserialized;
}
// --- Storage ---
function loadFilterRule(): FilterGroup {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (!saved) return fallbackRule;
return deserializeFilterGroup(saved);
} catch {
return fallbackRule;
}
}
// --- Component ---
export default function PersistentFilter() {
const { context } = useFilterSphere({
schema,
defaultRule: loadFilterRule(),
onRuleChange: ({ filterRule }) => {
localStorage.setItem(STORAGE_KEY, serializeFilterGroup(filterRule));
},
});
return (
<FilterSphereProvider context={context}>
<FilterBuilder />
</FilterSphereProvider>
);
}

You can build a preset collection that lets users save, switch between, and manage named filter presets. This is useful when users frequently reuse the same filter configurations.

If you prefer not to write custom serialization, you can use superjson which handles Date, Map, Set, RegExp, and other types automatically.