RefinerDB
RefinerDB is a javascript library that greatly simplifies creating advanced search solutions without requiring an additional backend data store.
To be honest, we're not exactly sure what to call the user experience (UX) that RefinerDB aims to simplify:
- Advanced Search?
- Faceted Search?
- Refiner Panel?
- etc...
Regardless of what you call it, we mean stuff like this:
Some commonalities we see in these UX scenarios are...
- Complex Filter criteria
- ex: Title contains 'day' AND genre eq 'Action'
- Dynamic Refiner Options w/ counts
- Based on the current filtered set, display the available refiner options and the number of items that match.
- Sorting, Paging, result counts etc..
RefinerDB will take care of all of this for you! You just pass in your full set of items and provide some info about the shape of your
data. Everything is done in the browser, so your app reacts super fast to
interaction and you aren't forced to complicate your architecture.
Code Example
import {
IndexType,
RefinerDBProvider,
useTextRefiner,
useMultiselectRefiner,
} from "refinerdb-react";
/**
* Tell RefinerDB about the shape of your data
* Image we had an array of movies like...
* [ { id:123, title: "Gladiator", genre: ["Action", "Drama"] }, ... ]
*/
const indexes = [
{ key: "genre", type: IndexType.String },
// For properties that generally have unique values, improve
// performance by specifying 'skipRefinerOptions'
{ key: "title", type: IndexType.String, skipRefinerOptions: true },
];
/** Wrap your app in a RefinerDBProvider */
export default function App() {
let movies = useData();
return (
<RefinerDBProvider name="movie-demo" indexes={indexes} items={movies}>
<div className="layout">
<div className="refiner-panel">
<TitleRefiner />
<GenreRefiner />
</div>
<div className="results-panel">
<ResultsView />
</div>
</div>
</RefinerDBProvider>
);
}
/**
* RefinerDB doesn't provide UI components,
* just a variety of hooks that are tailored for certain scenarios.
* The UI is compeletely up to you.
* */
function TitleRefiner() {
// This custom hook allows utilizing a "text contains" refiner
let { value, setValue } = useTextRefiner("title");
return (
<label>
Title
<input value={value || ""} onChange={(e) => setValue(e.currentTarget.value)} />
</label>
);
}
/**
* Here we build a basic Checkbox refiner
*/
function GenreRefiner() {
// This is the only RefinerDB code, the rest of this component is up to you.
let { getCheckboxProps, options } = useMultiselectRefiner("genre");
return (
<div>
<label>Genre</label>
{options.map((option) => (
<div key={option.key}>
<label>
<input {...getCheckboxProps(option)} />
<span className="checkable">
{option.key} ({option.count})
</span>
</label>
</div>
))}
</div>
);
}
Why RefinerDB and not X?
There are already some great offerings that can very capably power this UX.- Elasticsearch
- Algolia
- Appbase
- And many more...
However, these solutions generally...
- Require you to copy items from your database to their system.
- EITHER are tricky and time consuming to leverage their APIs to support this specific UX.
- OR provide UI components that are a little too prescriptive and hard to customize to specific UI needs.
- Introduce another element to the your app's architecture. For simple situations, this often feels like overkill.
RefinerDB runs completely in the browser.
- No replicating your data. RefinerDB doesn't care where your data comes from or what it looks like, it just expects an array of items with a primary key.
- It's really fast because there are no network calls to make with each change in criteria.
RefinerDB is headless.
- RefinerDB does not make any assumptions about your UI.
- RefinerDB aims to take full control of the "behavior" but the the presentation completely up to you.
- This means RefinerDB is fully compatible with any styling technique (component libraries like MUI, TailwindCSS, CSSinJS libraries etc...)
When is RefinerDB not a fit?
RefinerDB is designed and tuned to simplify simple scenarios, not power Walmart.com. It is a niche tool for when your data isn't at a scale to justify ANOTHER 3rd party service or database. RefinerDB's sweet spot is a dataset with 100-10,000 items.- Anything larger and you might be better served leveraging a server side solution.
- Anything smaller and you might be better off just rolling your own solution with simple filter functions
If you don't think RefinerDB is a fit for your scenario, check out these articles for an overview of what it would take to leverage a backend service.
Interactive Demo
This example leverages refinerdb-react, but there is a also vanilla JS version. The example is interactive, play around with it! import InteractiveDemo from "../features/sandpack/InteractiveDemo";