Pory

pory.iocontributed by Samuele Ongaro

YES

Replaceable in one session with an AI coding agent.

Turns a spreadsheet or table into a website: directories, listings and simple apps generated from rows, with filtering and detail pages.

Promptfree, for everyone, and the only version there is
Build me a site generated from my table, replacing Pory: a directory or a listing that updates when the data does, with filters and detail pages.

STACK
- Astro with a Node adapter — server-rendered so the site is current, with aggressive caching so it is fast
- SQLite through better-sqlite3 as the local mirror
- The source can be a spreadsheet, a table product's API, a CSV, or a database. One adapter interface, several implementations
- Caddy in front

THE SHAPE
- The data lives wherever it already lives. This pulls it, mirrors it locally, and serves pages from the mirror
- Serving directly from a spreadsheet API is the obvious approach and the wrong one: it is slow, it is rate-limited, and the site goes down when the source does
- So: sync on a schedule and on demand, serve from the mirror, and show the last successful sync somewhere the owner can see

THE DATA MODEL
- sources: id, kind, config_encrypted, sync_interval_seconds, last_synced_at, last_error, is_active
- collections: id, source_id, name, slug, table_ref, sync_cursor
- fields: id, collection_id, key, label, kind, is_filter, is_search, is_visible, position, format_json
- records: stored in a real table per collection, created and altered by generated migrations, keyed on a stable identifier from the source
- record_revisions: id, collection_id, record_id, field_key, old_value, new_value, at — so a change in the source is visible rather than silent
- sync_runs: id, source_id, started_at, finished_at, created, updated, deleted, status, error
- Identity comes from a column you nominate, never from row position. A sorted spreadsheet must not renumber every page on the site

SYNCING
- Incremental where the source supports it, full otherwise, with a cursor
- A record that disappears from the source is marked missing rather than deleted, and its page returns a clear gone state rather than a broken link. Somebody deleting a row by accident must not silently remove a page search engines have indexed
- Field types inferred on first sync and then fixed; a type change is a deliberate migration with a dry run
- Rate limits respected with backoff, and credentials encrypted at rest
- A manual sync button, and a webhook so the source can trigger one

THE PAGES
- An index with the records, a detail page per record at a permanent slug, and category or tag pages if the data has them
- The slug comes from a nominated field plus the stable identifier, and once published it never changes — a renamed row writes a redirect rather than breaking the link
- Filters generated from the fields marked filterable: checkboxes, ranges, a select. Server-rendered with the state in the query string, so a filtered view is a URL somebody can share and a search engine can index
- Search over the fields marked searchable, using FTS5, server-side, instant
- Sorting, and pagination that is real pages rather than an infinite scroll, because a directory's value is largely in being indexed

WHAT MAKES A DIRECTORY WORTH ANYTHING
- Every detail page has its own title, meta description, canonical URL and JSON-LD of the right type — LocalBusiness, Product, Event, Person, whatever the data actually is
- A sitemap generated from the records, split if it grows past the limit, and submitted
- An RSS feed of new entries
- A generated share image per record from its own title
- Fast: server-rendered HTML, no framework, under 100KB, with the page cached and invalidated by the sync
- Without these a directory is invisible, and being found is the entire business model of one

TEMPLATES
- A handful of layouts — directory, listing, gallery, map — each a real template in the repository rather than a configuration blob
- Fields mapped into template slots, so changing which column is the title is a setting rather than an edit
- Theme tokens: two backgrounds, two text colours, one accent, one border, all measured against WCAG AA
- Dark and light

SUBMISSIONS
- A form that writes a new row back to the source, or into a pending table for review
- Nothing published without review, ever. A directory anybody can add to is a directory full of spam within a week
- Honeypot, minimum time on form, rate limits per address hash

OPERATIONS
- .env: DATABASE_PATH, BASE_URL, SOURCE credentials, SESSION_SECRET, HASH_SALT
- Migrations on boot, each once
- Health endpoint reporting the age of the last successful sync and the last error — a stale site that looks fine is the failure mode here
- Nightly backup off the machine, restore script

WHAT MATTERS MOST
Stable identity and the search-engine surface. Nominate a real identifier column before anything else and never let a slug change, then get the sitemap, the structured data and the per-record titles right. A directory nobody can find is a spreadsheet with extra steps.

Give me the repository, one source adapter, migrations, .env.example, two templates, and a README with deploy steps behind Caddy.

What you lose

  • A live site that updates when the sheet does, with no deployment step
  • Templates for directories and listings that already look finished
  • Filtering, search and detail pages generated without configuration

If you would rather not build

  • Airtable with a published view, which is free

What it costs

as published on their pricing page

PlanBilled monthlyBilled yearlyLast read
—$15/mo——

Their pricing page is where these came from. Seeing a different price? Tell us.

The escape hatch

open source · no votes, no paid placement

Astro

$0

Content collections validate rows against a schema at build time.

withastro/astrofree · open source

Eleventy

$0

A small static generator that reads any data source you give it.

11ty/buildawesomefree · open source

Why this verdict

our own opinion · changed only by a person

86/100

Verdict yes at 86. A static site from a sheet is a build script. Validating rows and failing the build is what keeps it from degrading.

History

tracked since 10 Aug 2026 · nothing is ever overwritten

Interest · last 30 dayspeak 2/day
views01230 Aug4 Sept9 Sept14 Sept19 Sept24 Sept28 Sept
— views— prompt copies none yet— votes none yet

Questions about Pory

answered from the record above

Is Pory free?

No — the plan we track is $15 a month. Starter at around $15/month billed monthly, cheaper annually.

Can you replace Pory by building your own?

YES. Replaceable in one session with an AI coding agent. Replacement score 86 out of 100, build time one session. Read what you lose before you decide.

How much does Pory cost?

$15 a month on Starter — $180 a year. Recorded 10 Aug 2026.

What do you lose by replacing Pory?

A live site that updates when the sheet does, with no deployment step; Templates for directories and listings that already look finished; Filtering, search and detail pages generated without configuration. If any of those carry weight for you, keep paying.

Is there an open-source alternative to Pory?

Yes: Astro, Eleventy. The prompt on this page is for when you want it your way instead.

Related entries

same category first, most replaced first

All 16 in No-code apps & databases

Not sending yet

Every week, something stops being worth paying for.

New verdicts, prices that moved, entries added. One email a week. Unsubscribe in one click. Nothing is being sent yet — your address is kept here, and the first issue is the first thing it is used for.

free forever · no tracking pixel · stored here, never passed to anyone

Esc