Build Instapage

KINDAreplaces $99/mosaves $1,188/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A landing page builder whose hard problems are publishing static files without a broken minute, measuring speed from real visits rather than a lab score, and running split tests that are decided in advance instead of stopped when they look good.

estimated effort multi-daythe files for this build are in the project pack

ContentPages as ordered sections in SQLite, rendered to static HTML at publishRenderingOne template per section kind, server-side, no client framework on the published pageMeasurementFirst-party field collection from the published page using the standard web vitals libraryExperimentsDeterministic bucketing from a first-party visitor identifier, decided sample sizePublishingBuild into a new directory and swap, keeping previous versions

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the editor and the build.

    Get it nodejs.org, or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. have readya few pounds a month plus the domain

    Why The published pages need a stable address with a certificate, and the measurement endpoint has to be first-party to avoid being blocked.

    Get it Any small virtual server plus a domain, with automatic certificates.

    Verify The page loads over your domain with a valid certificate and a measurement reaches the endpoint

  3. installfree

    Why Computing these metrics by hand from performance entries is subtly wrong, and the library is the reference implementation of the definitions.

    Get it Add the published library to the page bundle and report to your own endpoint. open ↗

    Verify A real page load produces values for all three metrics

  4. have readyfree

    Why Every layout shift and every slow paint is invisible on a developer machine on a fast network, which is why pages ship broken on phones.

    Get it An old phone, or your browser's device and network throttling.

    Verify The page has been loaded once on a throttled slow connection and the shift measured

  5. decidefree

    Why These are the difference between an experiment and a scoreboard, and choosing them after the data arrives means choosing the answer.

    Get it Decide the minimum effect worth detecting, compute the sample size it needs, and write the stopping rule down.

    Verify Both are stored on the experiment record and shown in the report

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- pages(id, slug, title, status, current_version_id, created_at)
- page_versions(id, page_id, ordinal, sections_json, theme_json, built_at, built_by, notes) · every publish is a version you can go back to
- sections(kind, schema) · hero, features, testimonial, form, footer, each with a declared field schema
- experiments(id, page_id, name, variant_a_version_id, variant_b_version_id, metric, planned_n, started_at, stopped_at, stop_reason)
- assignments(visitor_id, experiment_id, variant, assigned_at) · deterministic from the identifier, written once
- events(id, page_id, experiment_id, variant, kind, at, session_id) · view and conversion, nothing else
- vitals(id, page_id, version_id, metric, value, rating, device, at) · one row per reported measurement from a real visit
- submissions(id, page_id, version_id, fields_json, received_at, spam_score, ip_country) · form entries, kept separately from analytics
- A published page is a directory of files. The database is the editor's state, not a dependency of the live page.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/pages.dbEditor state, versions, experiments and measurements. Not needed to serve a published page.
BUILD_DIRrequired./data/buildsOne directory per build. The live directory is a pointer to one of them.
KEEP_BUILDSrequired10How many previous builds to retain for rollback.
BUDGET_KBrequired180Total transferred bytes for a page. The build fails above it.
VITALS_ENDPOINTrequiredhttps://yourdomain.example/vFirst-party, same domain, so the measurement is not blocked as third-party.
EVENT_RETENTION_DAYSrequired180Page views and experiment events. Separate from submissions on purpose.
SUBMISSION_RETENTION_DAYSrequired730Form submissions. Longer, and deleted per person on request.

The build, in order

  1. Sections in, static files out

    A page is structured data that renders to HTML, CSS and images with no runtime.

    1. Fields you can validate, not markup you cannot change later.

      Files src/sections/schema.tssrc/sections/templates/

    2. Byte-identical output is how you know a rebuild is safe.

      Files src/build.tstests/build.test.ts

    3. That constraint is the entire performance story.

      Files scripts/serve-static.sh

    done when · tick each as it passes
    watch out
    • Storing the page as a blob of HTML from a rich editor means the day you change the heading style you cannot change it anywhere, because the markup is copied into every page and half of it was pasted from a document with its own inline styles. Store fields against a schema and render them, so a template change reaches every page that uses it.
  2. Fast for the visitor, measured on the visit

    The page meets the published thresholds where it counts, and the numbers come from real loads.

    1. A budget checked by a person is a budget that slips.

      Files src/budget.tstests/budget.test.ts

    2. The fallback relayout is the most common shift on a landing page.

      Files src/theme/fonts.csssrc/build.ts

    3. Two of the three metrics cannot come from a lab run.

      Files src/public/vitals.jssrc/api/vitals.ts

    done when · tick each as it passes
    watch out
    • A perfect lab score and a page that is not in any field dataset feels like proof, but a synthetic run cannot measure responsiveness because nobody clicked anything, and it only sees layout shifts above the fold during load, while a page with little traffic may never appear in a public field dataset at all because inclusion requires a minimum number of visitors that is deliberately undisclosed. Collect your own field measurements from the published page and treat the lab run as a smoke test.
  3. Publishing without a broken minute

    A publish is atomic and reversible, and a failed build never reaches the URL.

    1. Never write into the directory being served.

      Files src/publish.ts

    2. Request the page and its assets from the new directory first.

      Files src/publish.tstests/publish.test.ts

    3. The rollback nobody has run is not a rollback.

      Files scripts/rollback.shtests/rollback.test.ts

    done when · tick each as it passes
    watch out
    • Publishing by writing files over the live directory means the page is half old and half new for the seconds it takes, and a build that dies partway leaves it broken until somebody notices, which on a campaign page means the traffic you paid for hit an unstyled page. Build elsewhere, verify, then swap, and keep the previous build to roll back to.
  4. Split tests decided in advance

    An experiment has a sample size and a stopping rule before it starts, and the same visitor always sees the same variant.

    1. Sticky assignment with no lookup and no cookie race.

      Files src/experiments/assign.tstests/assign.test.ts

    2. Deciding it afterwards is deciding it from the data.

      Files src/experiments/create.ts

    3. And label any early stop wherever the number is shown.

      Files src/experiments/report.ts

    done when · tick each as it passes
    watch out
    • Watching the dashboard and stopping the moment one variant looks ahead turns a coin-flip difference into a decision, because checking repeatedly gives you many chances to catch a random lead, and on a page with a few dozen conversions a week that lead appears constantly. Fix the sample size when you create the experiment, show progress towards it, and make any early stop record who stopped it and why.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more website builders.