Dashboard

How to Add Saved Views and Filters to an AI-Built App

Saved views let users store a named combination of filters, sort order, and visible columns instead of rebuilding the same search every time. This guide covers the data model, team sharing, and what to do when a filtered field changes.

Steve Jefferson
Steve Jefferson
Developer Advocate
24 September 20261 min read

Saved views let a user store a specific combination of filters, sort order, and visible columns as a named preset they can return to with one click, things like "My overdue invoices" or "Active clients only." That is different from a basic search box, which forgets everything the moment the page reloads. To add saved views to an AI-built app, you need three pieces: a data model that stores the view's state as structured data tied to a user or a team, a UI that lets someone save the current table state and rename or delete it later, and a plan for what happens when a saved view points at a field that gets renamed or removed.

A search box remembers nothing: the user types a query, gets a result, and starts from scratch next time. A filter panel is a small improvement, since it remembers state only until the tab closes. A saved view is a persisted object, a name plus a set of filter conditions, a sort order, and a column selection, stored against a user's account so it survives a logout, a new browser, or a new laptop. If basic search does not exist yet in your product, that usually comes first, see our guide on adding search to an AI-built app. Saved views typically sit on top of that same filtering engine, they just add a persistence layer and a label.

That distinction matters for the data model. A one-off filter is transient client state, and it can live in the URL query string or component state without ever touching the database. A saved view is a first-class record. It needs its own table, its own permissions, and its own lifecycle, because someone will expect it to still be there next Tuesday.

Where Saved Views Live in the Data Model

The core table is simple. Something like:

  • id (primary key)

  • owner_id, the user who created it

  • team_id, nullable, set when the view is shared

  • name, the label the user typed, e.g. "Active clients only"

  • filters, jsonb, the filter conditions

  • sort, jsonb, field and direction

  • visible_columns, jsonb, an ordered array of column keys

  • is_shared, boolean, whether teammates can see it

  • is_default, boolean, whether it loads automatically

  • created_at and updated_at

Store filters and sort as jsonb rather than a rigid set of columns. The shape of a filter, a field, an operator, a value, and sometimes a list of values, is the same across nearly any resource type, so one flexible schema handles invoices, clients, tickets, or whatever your app manages, without a new table for every entity.

The decision that actually shapes this feature is per-user versus shareable. A per-user view is private by default, cheap to reason about, and never surprises anyone. A shareable, team-wide view is more useful in practice, since a support lead wants everyone using the same "Escalated tickets" view, but it introduces ownership questions: who can edit a shared view, what happens if they edit it out from under someone who relies on it, and whether an edit creates a fork or changes the original for everyone. The simplest workable rule is that shared views are edited only by their owner or an admin, and anyone who wants a variant clones it into a private view first. That single rule avoids most of the support tickets this feature generates. If your app already has an admin dashboard, that is the natural place to manage and audit shared views, see our guide on building an admin dashboard for an AI-built app.

What Happens When the Underlying Field Changes

This is the part most builders skip until it breaks in production. A saved view's filters reference specific fields, for example status equals "overdue" or assigned_to equals current user. If someone later renames that field, splits it into two fields, or deletes it outright, every saved view filtering on it is now pointing at nothing.

  • Reference fields by a stable internal key or id, never by the display label a user sees. Renaming a label should never break a filter.

  • When a field is deleted or its type changes incompatibly, do not silently drop the condition. Flag the view, something as simple as a small warning badge, and show which condition is now invalid.

  • Keep the last known-good version of a view's filter set so a user can roll back after an accidental edit, instead of rebuilding the view from scratch.

  • Run an integrity check whenever a schema change happens, so broken views surface immediately instead of being discovered by a confused user weeks later.

None of this needs to be complicated. It just needs to exist. A saved view that can silently drift out of sync with the schema, and fail with no explanation, is worse than not offering saved views at all.

Give Every New User a Sane Default View

A user who logs in for the first time and sees an empty table with no filters applied is looking at raw data, not a product decision. Ship one default view for every new account, seeded automatically, based on what someone in that role most likely wants to see, something like "My open items" for an individual contributor or "All active" for an admin. Mark it as the default so it loads first, and let a user change their own default later without losing the option to load a wider, unfiltered view. This is the same instinct that makes a good usage dashboard useful on day one instead of empty. Someone should never have to configure a feature before they can see why it matters.

What to Give an AI Builder for This Feature

If you are prompting an AI coding assistant to build this, a vague instruction like "add saved views" produces something shallow: usually a favorited search with no team logic and no handling for schema drift. Be specific about the pieces above, and treat this as part of the broader work of building an app with AI rather than a one-line feature request. And if the app also needs to work without a live connection, remember that a saved view's filters run against local data too. an app that also needs to work offline is worth reading before you assume saved views behave identically with no connection.

  1. Define a saved_views table with an owner, an optional team, filters, sort order, and visible columns, plus separate is_shared and is_default flags.

  2. Specify that filters reference field keys, not display labels, and that renaming a field's label must never break an existing view.

  3. Require a full UI flow: save, rename, duplicate, share, set as default, and delete, not just a save button.

  4. Ask for defined fallback behavior when a filtered field no longer exists: flag the view, do not silently return an empty or unfiltered result set.

  5. Seed one default view per new user or role at account creation, not an empty table.

  6. Log who created and last edited a shared view. Shared state with no audit trail becomes a support problem the first time someone asks who changed this.

Frequently Asked Questions

Should saved views be stored client-side or in the database?

In the database, tied to the user's account. Client-side storage like localStorage means the view disappears on a new device or a cleared browser, which defeats the point of naming and saving it.

Can a saved view include more than filters, like column widths?

Yes, and most production implementations do. Column order, visible or hidden columns, and sort order are cheap to store alongside filters in the same JSON blob, and users expect a "view" to restore the whole layout, not just which rows show up.

What happens to a shared view if the owner leaves the team?

Decide this before it happens: either transfer ownership to a team admin automatically, or freeze the view as read-only until someone claims it. Leaving it orphaned with no owner is how shared views quietly rot.

How many saved views should a user be allowed to create?

Enough that it does not feel arbitrary, with a soft cap, something like 20 to 30 per user, mainly to keep the picker UI usable rather than to save on storage. Jsonb rows are cheap; a cluttered dropdown is the actual cost.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.