- 🔄 Centralized State System - Efficient application state management
- 🎨 Headless UI - Flexible, unstyled components for full customization
- 🧩 Composable Components - Build complex UIs with simple, reusable parts
- 🔗 URL Sync - Built on nuqs for robust URL state management
- 🛠️ Developer-Friendly API - Intuitive design for seamless development
- ⚡ Performance Optimized - Smooth handling of large datasets
# Using npm
npm install react-qif nuqs
# Using yarn
yarn add react-qif nuqs
import { useFilters, FiltersProvider, useFiltersContext } from 'react-qif';
import { parseAsString } from 'nuqs';
type FiltersType = {
search: string;
category: string;
};
const App = () => {
const filtersInstance = useFilters({
parsers: {
search: parseAsString.withDefault(''),
category: parseAsString.withDefault('test')
}
});
return (
<FiltersProvider instance={filtersInstance}>
<SearchFilter />
</FiltersProvider>
);
};
const SearchFilter = () => {
const { getValue, setValue } = useFiltersContext<FiltersType>();
return (
<input
value={getValue('search') || ''}
onChange={(e) => setValue('search', e.target.value)}
/>
);
};
import { NuqsAdapter } from 'nuqs/adapters/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
<NuqsAdapter>{children}</NuqsAdapter>
</body>
</html>
);
}
import { NuqsAdapter } from 'nuqs/adapters/react';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<NuqsAdapter>
<App />
</NuqsAdapter>
</StrictMode>
);
-
instance
: Filter instance created by useFilters -
children
: React nodes
interface UseFiltersOptions<T extends FiltersValue> {
syncWithSearchParams?: boolean;
parsers: UseQueryStatesKeysMap<T>;
}
Returns an object with the following methods:
-
register(name, defaultValue)
: Register a filter with default value -
unregister(name)
: Remove a filter -
setValue(name, value)
: Update filter value -
getValue(name)
: Get current filter value -
reset()
: Reset all filters to defaults -
isResetDisabled
: Check if reset is available
Unlike nuqs, Qif also supports client-side filtering. By setting syncWithSearchParams
to false
, no parameters will be added to the URL. This is useful for temporary filters or when you don't want to persist the filter state in the URL.
const App = () => {
const filtersInstance = useFilters({
syncWithSearchParams: false, // Disable URL synchronization
parsers: {
search: parseAsString.withDefault(''),
category: parseAsString.withDefault('all')
}
});
return (
<FiltersProvider instance={filtersInstance}>
<ProductFilters />
<ProductList />
</FiltersProvider>
);
};
const ProductFilters = () => {
const { getValue, setValue } = useFiltersContext();
return (
<div className="filters">
<input
value={getValue('search') || ''}
onChange={(e) => setValue('search', e.target.value)}
placeholder="Search products..."
/>
<select
value={getValue('category') || 'all'}
onChange={(e) => setValue('category', e.target.value)}
>
<option value="all">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
</div>
);
};
const ProductList = () => {
const { getValue } = useFiltersContext();
const searchTerm = getValue('search') as string;
const category = getValue('category') as string;
// Filter your products based on searchTerm and category
const filteredProducts = products.filter(product => {
const matchesSearch = product.name.toLowerCase().includes(searchTerm.toLowerCase());
const matchesCategory = category === 'all' || product.category === category;
return matchesSearch && matchesCategory;
});
return (
<div className="products">
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
};
This example demonstrates:
- Client-side filtering without URL parameter synchronization
- Multiple filter types (search input and category select)
- Real-time filtering of product list
- Type-safe filter values with TypeScript
Contributions, issues and feature requests are welcome! Feel free to check issues page.
MIT © Hasan Ardestani