Every app reaches a moment where something has to outlive the tab it was typed into. Where you put it decides who can read it, whether it survives your next deploy, and how much work it will be to move. Most small projects never make that decision. They inherit whichever storage the assistant reached for first, and find out what it meant months later.

The idea

Every piece of information in your app has a home, and the home decides who can see it and how long it survives. There are three realistic homes for a small project: a file that ships with your code, inside one visitor's browser, and a database on a server. Picking wrong is not fatal, but the cost of moving grows with every row you write.

code
  WHO SEES IT      ->     WHERE IT LIVES        ->    GONE WHEN
  ------------------------------------------------------------------
  everyone,               a file in your repo         you edit it and
  identically             content/*.mdx, .json        redeploy
  ------------------------------------------------------------------
  one person,             localStorage                they clear the browser,
  one browser,            ~5 MB, text only            switch to their phone, or
  one device                                          (Safari) go 7 days without
                                                      touching your site
  ------------------------------------------------------------------
  all your users,         SQLite                      that one server's disk
  as long as there        a single .db file           is wiped or replaced
  is only one server      on that server
  ------------------------------------------------------------------
  all your users,         Postgres                    you delete it, and
  many at once,           (Supabase, Neon, RDS)       your backups
  any number of servers

How it works

A file in your repository is read at build time. It is perfect for things that change when you change them: product pages, blog posts, pricing copy, config. It is public, it is in git, and a visitor can never add to it.

localStorage is a key-value notepad the browser keeps per site, per device, capped around 5 MB, and it only holds text. Good for a theme preference, a dismissed banner, a draft the user has not submitted yet. Bad for anything you need on their other device, anything two people share, and anything you would be upset to lose. Safari also clears it after seven days of browsing during which the person never interacted with your site, so it is a poor place for something they expect to find next month. The user can also open developer tools and edit it, so it is never a place to record what someone paid or what they are allowed to do.

A database is a separate program built to hold changing records for many people at once, answer questions about them quickly, and not lose them when your app restarts. SQLite is a database in a single file on one machine, with no server to run. Postgres runs as its own service and is what Supabase, Neon and most hosts give you. Both speak .

What to do

  1. Before you build a feature, say who owns the data and how long it must live. "Any visitor, until they delete it" means a database. "This one person, until they clear their browser" means localStorage.
  2. Default to Postgres for anything a user creates. Supabase gives you a hosted one in about two minutes. Its free plan, checked on 2026-08-12, covers 500 MB of database, 1 GB of files and two active projects, and it pauses a project after seven days with no database activity. A paused prototype is not a lost one, but you do have to go and wake it.
  3. Keep secrets and personal information out of the files you commit and out of localStorage. Both are readable by people who should not read them. Keys do live in a file on your machine, an .env.local that git is told to ignore, which Connecting other services covers.
  4. Know that a hosted Postgres like Supabase answers the browser directly, over its own API, using a key that ships in your pages. A table with no access rules on it is readable by anyone who looks. Turning that off is section 5, and it is the single most common way a small app leaks everything it has.

Where it breaks

The advice inverts for content. Putting your marketing pages in Postgres so they are "editable" adds a database call, a schema and a backup story to something a markdown file did better. SQLite is a serious database, not a toy, and it is a good answer for a single-server app, but it is a bad answer the moment your host runs two copies of your app or throws away the disk between deploys. A database is also not safety by itself. It protects the data you have actually backed up and put access rules on, and both of those are things you go and do.