Stop losing the links people send you
Someone sends you a post worth keeping. You save it. Six weeks later you know you saved something about this and you cannot find it anywhere: not in the app's own saves, not in your notes, not in the twelve tabs. I stopped trying to fix that with another Notion database and built a small thing that does one job. It is called Shelf, and you can have your own in ten minutes or in an afternoon.
before the how
What Shelf is, either way
One home for the links, docs and PDFs people send you. A card grid you can search, filter and open. Nothing revolutionary — the point is that it fits how you actually save things, because you are the one who specified it.
Both versions share the same three decisions, and the first one is the reason it stays usable six months in rather than being abandoned by week three.
side by side
Which one to build
| Basic | Pro | |
|---|---|---|
| Time to a working thing | 10 minutes | an afternoon |
| Nothing to sign up for, nothing to deploy | ✓yes | ✗no |
| Save links, Google Docs and PDFs | ✓yes | ✓yes |
| Search, topic and type filters, favourites | ✓yes | ✓yes |
| Reader view, light and dark themes | ✓yes | ✓yes |
| Export everything as a backup file | ✓yes | ✓yes |
| Survives clearing your browser | ✗no | ✓yes |
| Same library on laptop and phone | ✗no | ✓yes |
| Browser extension: saves the caption and your highlight | ✗no | ✓yes |
| Save straight from your phone's share sheet | ✗no | ✓yes |
| Posts and videos playing inside the reader | ✗no | ✓yes |
| Add and rename your own topics from the app | ✗no | ✓yes |
| Undo a deletion | ✗no | ✓yes |
| Files larger than about 3 MB | ✗no | ✓yes |
Two honest sentences to decide on. Basic is not a demo — it is a real tool, and if your saving happens at a desk it may be all you ever need. Pro exists because mine did not: the things worth keeping arrive on my phone, and a library I can only add to from one laptop is a library I stop adding to.
pick one
Which version are you building?
the ten minute version
Basic
One index.html with everything inside it. You open it by double-clicking the file. There is no server, no account, no hosting bill and nothing to keep running. Your library lives in that browser, on that machine.
Paste the prompt below into Claude, ChatGPT or whatever writes code for you, and ask for the file.
Build me a single-file personal content library called Shelf. One
index.html with the CSS and JavaScript inline. No framework, no build
step, no server. I open the file from my own machine and it works.
WHAT IT IS
One home for the links, Google Docs and PDFs people send me, so they
stop getting lost. Tagline: everything worth keeping, in one place.
ONE SAVED ITEM
id, title, url, source, type, topic, purpose (one line on why it is
worth keeping), summary (longer notes, becomes the reader body),
savedAt, fav.
Keep topic, source and type as three separate things. They answer
three different questions: what it is about, what I will do with it,
and where it came from. Do not merge them into one tags field.
- Topics, seeded: Marketing, Design, AI, Productivity, Life, Finance.
Each gets its own colour from a shared palette.
- Types, seeded: Guide, Template, Checklist, Tool, Reference.
- Source is never asked for. Read it off the URL: Threads, Instagram,
YouTube, TikTok, X, Substack, LinkedIn, Reddit, GitHub, Google Doc,
Notion, PDF by extension, otherwise Web page. An uploaded file is
always source File, whatever URL is attached to it.
ADDING SOMETHING
Drop a link or a file anywhere on the page, or press an Add button.
Both open the same form: link, title (suggested from the URL, I can
edit it), source shown as a read-only fact rather than asked, type,
topic, a one-line notes field, and a longer details field that becomes
the reader body. Only the title is required.
FINDING IT AGAIN
A left rail: full-text search over title and notes, multi-select topic
chips, single-select source and type built from what I actually have,
a favourites toggle, and a clear-all link that only appears when
something is on. A card grid: source badge, title, two-line excerpt,
coloured topic dot, type label, star button. Clicking a card opens a
reader view with the full notes and a link to the original.
STORAGE
Browser localStorage. No accounts, no server. Guard file uploads at
about 3 MB and say clearly when one is too big rather than failing
silently.
Give me an Export button that downloads everything as one JSON file,
so my library is never trapped inside a browser I might clear.
THE BAR
Light and dark themes, following my system setting, with a manual
toggle. Real copy everywhere, never placeholder text. Every colour
combination readable at WCAG AA. Everything reachable by keyboard with
a visible focus ring. No horizontal scrolling at any width. Respect
prefers-reduced-motion.
Build it, then tell me the first thing you would fix.
ten minutes, honestly
What you actually do
- Paste the prompt and ask for the file. Nothing else. Do not explain the project first; the prompt does it better than a chat window will.
- Save
index.htmlsomewhere you will find it. Not Downloads. Somewhere with a name, because this is a tool now, not an experiment. - Double-click it. It opens in your browser and it is running. There is no step four to deploy anything.
- Bookmark it and put the bookmark on your bookmarks bar, at the far left. The whole design lives or dies on saving being cheap.
- Save three real things. Not test entries. Three links you would have lost, with real notes. Then export the JSON once, so you have seen where your backup lands.
what will eventually annoy you
The ceiling
Everything lives in one browser on one machine. That is fine until one of four things happens, and one of them will.
None of that is a reason to skip Basic. It is a reason to export your JSON before you decide. When one of the four starts costing you more than an afternoon would, the Pro version is on the other tab.
the full version
Pro
This is the one I use. Three source files and a small build step, deployed to a free host, with your library in your own Google Drive and a browser extension feeding it. It is too long to copy off a page, so it comes as a file.
Not a prompt to tweak — a specification that walks the assistant through the build in five phases and stops at every point where it needs you. Open a new project in your coding assistant and give it the file whole. Do not summarise it, do not paste the interesting parts. Almost every line is a decision, and the ones that look like padding are usually the ones stopping a default you do not want.
what happens when
The five phases
The document tells the assistant to work in this order and to stop and show you the result at the end of each phase, rather than vanishing for an hour and returning with something that has never been run.
- The app, on your own machine, with no syncing yet. Everything in the browser's own storage. You get the add form, the grid, search, filters and the reader view. This is the phase where you find out you wanted the notes field bigger, or that you never use favourites. Change it here, while there is almost nothing to break.
- Deploy it. The assistant sets up the build, then hands back to you: connect the repo to Vercel, Netlify or GitHub Pages and copy the live address. This comes before the Google work and not after, because Google's setup asks for the exact address your app runs at, and until it is deployed you do not have one. Doing these two in the wrong order is the most common way to lose an hour here.
- Google Drive. Explained properly below. This is the fiddly one.
- The browser extension. Also below. It is what makes saving cheap enough that you keep doing it.
- Phone access, then the quality pass. The share-sheet setup, then the accessibility and quality checks run as an actual list, with a report of what passed rather than a claim that everything is fine.
phase three, properly
Why Google is in this at all
The app has no server and no database. That is the whole design: nothing to pay for, nothing to maintain, nobody who can shut it down or start charging you. But it means the library has to physically live somewhere, and the browser's own storage is not that somewhere — clear your cache, switch laptops, or open it on your phone, and it is gone. That is the ceiling the Basic version runs into.
So your Drive is the storage. The app creates one folder, keeps a single index file inside it holding your whole library, and puts any uploaded files there too. You can open that folder yourself and look at it. If you ever delete the app, your things are still sitting there.
Connecting it is the half hour that will annoy you. You are doing something that feels disproportionate — creating a project in Google Cloud Console, turning on the Drive API, filling in a consent screen, generating a client ID — for what is a personal tool. There is no shortcut. What you get for it is the permission scope: the app may see only files it created itself, and nothing else in your Drive. Not your documents, not your photos, not the tax folder.
You will also notice there is no password and no client secret anywhere. This flow does not use one, so if you find yourself hunting for a secret to paste in, you have taken a wrong turn in the console.
phase four, properly
What the extension is for
A web page is not allowed to read the contents of a different website. That is a browser security rule and a good one. But it means your app, sitting at its own address, cannot see the caption of the Instagram post you are trying to save, and most social platforms hand back nothing useful to the outside either. So without help, a saved post arrives as a bare link with no text, which makes it unfindable three weeks later — the exact problem you were solving.
The extension runs inside the page you are standing on, so it can see what the page says. It reads three things and passes them across:
Three ways to trigger it, all doing the same thing: the toolbar button, the right-click menu, and a keyboard shortcut you set yourself. Set the shortcut. It is the difference between saving things and meaning to.
the part I nearly left out
Using it from your phone
Most of what people send you arrives on your phone, which is also where you are least likely to open a laptop to file it. The app handles this without a mobile app existing, because it accepts an address of its own that carries the link you want to save.
The shape is ?add= followed by the link, with an optional title and text. Anything that can open a web address on your phone can therefore feed it.
- Add it to your home screen first. Open your deployed address in the phone browser and use "Add to Home Screen". It opens like an app, signed in, and that alone removes most of the friction.
- On iPhone, build a two-action Shortcut. In the Shortcuts app: a new shortcut, turn on "Show in Share Sheet" and set it to accept URLs. Then one Text action containing your address plus
?add=followed by the Shortcut Input, and one "Open URLs" action after it. Name it something short. It now appears in the share sheet of every app on the phone. - On Android, it can register as a share target directly, so your app shows up in the normal share menu alongside everything else.
- Either way, ask the assistant to set this up with your real deployed address rather than working it out yourself. It is the last phase in the document and it takes two minutes with the URL in front of it.
the honest part
What you are accepting
Three limits, all stated in the document, none of them dealbreakers as long as you know about them going in.
None of these is a bug to be fixed later. They are the price of a thing with no server and no database, which is also why it costs nothing to run and why nobody can shut it down.
worth more than the app
How to write one of these for something else
You will build Shelf and then want a different small tool. The document is the transferable part, so it is worth knowing why the long version works when "build me a bookmarks app" does not.
- Say what it is in one sentence, before any technical detail. Everything downstream gets judged against that sentence. A spec that opens with the tech stack has already lost the thread.
- Name the data model early. What one record looks like is the decision every other decision hangs off. Write it down and half the ambiguity disappears.
- Say what you do not want. The line that keeps "Post" and "Video" out of the type list is doing more work than most of the feature descriptions, because it stops a default that would have arrived without being asked for.
- Explain the why for anything non-obvious. The paragraph about why the extension has to exist is not documentation, it is insurance. Without a reason, a capable assistant will helpfully decide the extension is redundant and drop it.
- State the quality bar as a requirement. Accessibility, real copy instead of placeholders, no horizontal scroll. Written down they get built in. Left out they get retrofitted, badly, by you, later.
- Document the limitation you are accepting. Otherwise you get an over-engineered answer to a problem you were happy to live with.
The second one is not harder to write. It is just written after you have thought about it, which is the actual work and always was.