BASE: A Database, Not a Spreadsheet
A grid will let you type anything anywhere, which is why so much of the world's data is a date column with the word soon in it. Here a column has a type and the table can say no. Records with typed fields, a row editor, saved views with their own filters and sorting, CSV import that works out what each column is and shows you its reasoning, and export that can be read back. All of it in this browser, none of it uploaded.
Tables
A table is a set of records with the same shape. Every column has a type, so a date column holds dates and a number column holds numbers to a stated number of decimal places, and the grid can say no to anything else.
Import a CSV
Every column is read, guessed at, and the guess is shown with the reason for it. Nothing is committed until you have looked.
Drop a .csv here, or
What each column was taken to be
Table
Columns, sorting and filters
Columns
Add a column
Sort
Filters
What this is, and what it is not
A column has a type, and that is the whole point. A spreadsheet will let a date column hold the word soon, a number column hold twelve and a half thousand written out in words, and a status column hold Sent, sent and Sent. on three different rows. None of those can be sorted, filtered or added up, and every one of them is somebody's afternoon a year later. Here a date is a date, a number is a number to a stated number of decimal places, and a single select can only hold one of the options somebody decided on.
Numbers are added up as whole units, not as fractions. A column set to two decimal places is totalled as a whole number of hundredths and only turned back into a decimal at the end, the same trick LEDGER uses for pence. Adding 0.1 and 0.2 as ordinary floating point gives 0.30000000000000004, and a total that is wrong in the seventeenth digit is a total somebody eventually has to explain. Averages are worked to two places more than the column keeps, so an average of whole numbers is not silently rounded back to a whole number.
A link points at a row, not at a copy of its name. A link column holds the identity of a row in another table, so the client's name is written once. Rename the client and every invoice shows the new name, because the invoice never held the name in the first place. Delete a row and you are told how many links point at it before anything happens; delete a whole table and the link columns pointing into it turn back into plain text rather than being left pointing at nothing.
A view is a question, saved. Which columns, in what order, sorted how, filtered how, and whether it is a table or a gallery. Every view belongs to its table and is kept with it, so the question you ask every Monday is one click rather than five, and nobody has to keep a second copy of the data with the filter already applied to it.
Nothing moves under your hands. Editing a cell changes the value, the same value in the side panel, and the figures that depend on it. It does not re-sort the table, re-run the filter or rebuild the grid, because all three would pull the row you are working in out from under the cursor at exactly the wrong moment. The view asks its question again when you next change the search, the sort, the filters or the view itself.
The import tells you what it guessed and why. Each column is read and the type is inferred from the values, with the reason printed next to it: all twelve values are numbers, or every value could be read either way round so day/month/year was assumed. You can change any of them before committing, and nothing is created until you do. A guess presented as a fact is how a column of part numbers becomes a column of dates.
The CSV it writes can be read back. A field containing a quote, a comma, a line break or a bare carriage return is wrapped in quotes with its own quotes doubled, which is RFC 4180 and what every spreadsheet expects. The bare carriage return is the one usually forgotten, and it is what arrives in a cell pasted out of an older file. Rows end with a carriage return and a line feed for the same reason.
It is a database for a person, not for a company. Everything lives in this browser under one key, so there is no account, no sharing, no permissions and no audit trail, and there is a limit to how much a browser will hold: some thousands of rows rather than some millions. Export is the backup, and the JSON carries the column types so you can see exactly what was held. A big table is drawn three hundred rows at a time, because every cell on the screen is a live field and forty thousand of them make a page that will not take a keystroke; the count, the figures under the grid and both exports still describe every row the view matches, not just the three hundred you can see.
Questions people ask
How is this different from SHEETS?
A spreadsheet is a grid of cells where anything can be anything and the meaning lives in the formulas. A database is a set of records with a stated shape, where the meaning lives in the columns. If you are modelling a calculation, you want the grid. If you are keeping a list of things of the same kind, and you keep wishing the grid would stop letting you type nonsense into it, you want this.
What types can a column be?
Text, long text, number with a set number of decimal places, date, checkbox, single select with defined options and colours, multi select, URL, and a link to a row in another table. You can change a column's type at any time under Columns, sorting and filters; the values you already have are re-read as the new type and anything that cannot be read stays as it was typed.
Can I edit in the grid or do I have to open a row?
Both. Every cell in the grid is editable where it sits, including the selects and the checkboxes, and the tags in a multi select are added and removed from the cell itself. The side panel gives the same row one field at a time with room to write, which is where the long text fields are actually usable.
What does the export contain?
Exactly what the view in front of you shows: those columns, those rows, in that order, after the filters and the search. The line under the export buttons says how many of each, so there is never a question of whether a filter was still on. The JSON also carries the type of every column and the options of every select.
What happens if I delete something?
You are asked first, and told what will go: how many rows and columns a table holds, how many filled values a column holds, and how many links from other tables point at the row or the table you are about to remove. Those links are then emptied rather than left pointing at nothing. There is no undo, so the export is the safety net.
A row I have just edited still shows, though it no longer matches the filter. Why?
Because it would be worse the other way. The browser tells a page that a cell has been finished with at the moment you click the next one, so re-running the filter there would delete the row, and with it the cell you were aiming at, half a keystroke before your typing arrived in it. Rows therefore stay where they are while you work and the view re-asks its question when you next change the search, the sort, the filters or the view. The figures at the foot of each number column follow every keystroke regardless, so the totals are never behind what you can see.
Why can it not open my Excel file?
Because reading the xlsx format properly means a library, and every library this site serves has to be vendored and pinned by hand. Save the sheet as CSV, which every spreadsheet does from its own save menu, and this reads it and works out the types. CLEANSHEET is the tool for looking inside an xlsx before you share it.
Where is the data, and how much can it hold?
In this browser's local storage, under one key, on this device, and nowhere else. Browsers give a site a few megabytes of that, which in practice is thousands of rows rather than millions. Export the JSON if you want a copy that outlives the browser.
Can two people share a table?
No, and that is deliberate: sharing needs a server, a server needs accounts, and the whole point of this site is that it asks nobody for anything. Export the CSV or the JSON and send it the way you would send any other file.