Schema
The widget is not the column type
A widget decides how a value is typed in and drawn — a date picker, an email link, a currency reading. It sits on top of a plain database column, and the formatting it adds lives in the browser, so the API and the export still see the raw value underneath.
A column in SchemaStack has two separate ideas glued to it, and it helps to keep them apart. One is the database type — VARCHAR(255), NUMERIC(10,2), TIMESTAMP — the real thing Postgres stores. The other is the widget: how that value is entered and how it is drawn. The widget is what most tools call a "field type," and it is not the same as the storage type.
Sixteen widgets over ordinary columns
There are sixteen: STRING, TEXT, INTEGER, DECIMAL, DATE, DATETIME, BOOLEAN, EMAIL, URL, PHONE, SELECT, MULTI_SELECT, FILE, IMAGE, UUID, and RELATIONSHIP. Each one knows the column it wants to create — EMAIL and URL are both VARCHAR (255 and 2048), PHONE is VARCHAR(50), DECIMAL is NUMERIC(10,2), DATETIME is TIMESTAMP. So EMAIL doesn't create an "email column"; it creates a VARCHAR and then renders and validates it as an email.
That separation is the useful part. The database stays honest — anything else connecting to it sees a VARCHAR, not a proprietary type — while the UI gets a mailto link, a date picker, a Yes/No chip, a thumbnail with a lightbox.
The value that comes back is raw
Here is the part that surprises people, and it is deliberate. When SchemaStack hands a value to the frontend, it hands it untransformed. The email comes back as the email, not mailto:[email protected]. The boolean comes back as true, not Yes. The number comes back as 10300.00, not $10,300.00.
The formatting you see — currency, percent, the 16 Apr 2014 date style, the prefix and suffix, the uppercasing — is a display option applied in the browser. The reason is that a cell you can edit has to show the stored value, or editing would round-trip through a formatted string and back. So standalone cells stay raw and the frontend formats them for reading.
The consequence worth stating plainly: a currency-formatted column looks like $10,300.00 in the grid and comes back as 10300.00 from the REST API and in an export. The presentation is a reading, not a property of the data. That is usually what you want — the number is the number, and every consumer decides how to show it — but it does mean the pretty version lives only where a person is looking.
The type is guessed on the way in, and confirmed by you
When you import an existing schema, SchemaStack infers a widget from each column's type: TIMESTAMP becomes a DATETIME, BOOLEAN becomes a checkbox, NUMERIC becomes a DECIMAL. It also reads the column name for a hint — a VARCHAR called email becomes an EMAIL, one called phone becomes a PHONE, url or link becomes a URL. Inference suggests; you confirm. It never overrides a type you set.
Changing a widget can be free, or a migration
Some widget changes are pure presentation and cost nothing — turning a text column into a SELECT with a list of options doesn't touch storage, because a SELECT is a reading over the same VARCHAR. Others change the underlying type, and those are real ALTER COLUMN migrations. The app tells you which is which before you commit, and the dropdown of widgets you're offered is narrowed to the ones compatible with the column's current database type — you can't turn an INTEGER column into a date picker.
What it doesn't do (yet)
- No time-of-day widget. A
TIMEcolumn is imported and displayed as plain text — there is no time picker. It falls back to the string widget rather than guessing. - Currency and percent are display options, not widget types. They are formats on a number, applied in the browser. There is no "money" or "rating" or "color" widget; JSON and binary columns display as text.
- Display formatting stops at the browser. The REST API and exports return stored values, so a formatted column reads one way in the grid and another over the wire. If a downstream consumer needs
$10,300.00, it formats the number itself. DATETIMEhas no display-format option.DATEoffers ISO / medium / US styles; the date-and-time widget does not, yet.- Changing a widget can cost a migration. Only presentation-level changes (like switching to a
SELECT) are free; anything that alters the target database type rewrites the column, with the usual migration preview first. - A resync deletes a removed column's widget. Widget type, display options and validation rules live in metadata; if a column is dropped from the database and a sync removes it, that configuration goes with it. See schema drift for what is and isn't watched.
The columns guide covers every widget and its options; once a column exists, the REST API you didn't write exposes its raw value, formatting left to you.
Verified 26 Aug 2026: 175 tests across the widget suites, all green 2026-08-26 — WidgetConfigurationUnitTest (asserting every one of the 16 widget types has a registered configuration and emits a valid PostgreSQL column type), WidgetValueTransformerTest, SmartWidgetTypeStrategyTest (PostgreSQL and MySQL type inference plus the name-based email/url/phone refinement), ColumnMutationRulesServiceTest (the per-DB-type allowed-widget lists) and DisplayOptionsFormatterTest, run in metadata-test. The "formatting is a browser reading" claim comes from DisplayOptionsFormatter's own contract, which restricts server-side formatting to merged display-group cells..