Build Instapage
KINDAreplaces $99/mosaves $1,188/yrback to the verdict
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.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the editor and the build.
Get it nodejs.org, or your version manager. open ↗
Verify
node --version prints v22 or higher - 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 - 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 - 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 - 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.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_PATH | required | ./data/pages.db | Editor state, versions, experiments and measurements. Not needed to serve a published page. |
BUILD_DIR | required | ./data/builds | One directory per build. The live directory is a pointer to one of them. |
KEEP_BUILDS | required | 10 | How many previous builds to retain for rollback. |
BUDGET_KB | required | 180 | Total transferred bytes for a page. The build fails above it. |
VITALS_ENDPOINT | required | https://yourdomain.example/v | First-party, same domain, so the measurement is not blocked as third-party. |
EVENT_RETENTION_DAYS | required | 180 | Page views and experiment events. Separate from submissions on purpose. |
SUBMISSION_RETENTION_DAYS | required | 730 | Form submissions. Longer, and deleted per person on request. |
The build, in order
Sections in, static files out
A page is structured data that renders to HTML, CSS and images with no runtime.
Fields you can validate, not markup you cannot change later.
Files
src/sections/schema.tssrc/sections/templates/Byte-identical output is how you know a rebuild is safe.
Files
src/build.tstests/build.test.tsThat constraint is the entire performance story.
Files
scripts/serve-static.sh
done when · tick each as it passeswatch 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.
Fast for the visitor, measured on the visit
The page meets the published thresholds where it counts, and the numbers come from real loads.
A budget checked by a person is a budget that slips.
Files
src/budget.tstests/budget.test.tsThe fallback relayout is the most common shift on a landing page.
Files
src/theme/fonts.csssrc/build.tsTwo of the three metrics cannot come from a lab run.
Files
src/public/vitals.jssrc/api/vitals.ts
done when · tick each as it passeswatch 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.
Publishing without a broken minute
A publish is atomic and reversible, and a failed build never reaches the URL.
Never write into the directory being served.
Files
src/publish.tsRequest the page and its assets from the new directory first.
Files
src/publish.tstests/publish.test.tsThe rollback nobody has run is not a rollback.
Files
scripts/rollback.shtests/rollback.test.ts
done when · tick each as it passeswatch 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.
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.
Sticky assignment with no lookup and no cookie race.
Files
src/experiments/assign.tstests/assign.test.tsDeciding it afterwards is deciding it from the data.
Files
src/experiments/create.tsAnd label any early stop wherever the number is shown.
Files
src/experiments/report.ts
done when · tick each as it passeswatch 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.
Forms, the domain, and what you keepproduct builder
The page collects leads safely on your own domain, and every visitor record has a stated life.
One retention rule cannot serve both.
Files
src/forms.tssrc/retention.tsSize, honeypot, rate limit, then validation.
Files
src/forms.tstests/forms.test.tsA campaign page with an expired certificate converts at zero.
Files
docs/domain.md
done when · tick each as it passeswatch out- Putting form submissions in the same table as page events and then applying one retention rule deletes the leads along with the analytics, or keeps the analytics as long as the leads; either way somebody is unhappy and one of them is a data protection problem. Keep them apart, give each its own retention period, and write both in the README.
That is the whole plan for Instapage. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- A general-purpose drag and drop canvas.
- Audience personalisation.
- Heatmaps and session recording.
- Multi-user editing and approvals.
- general-purpose visual editing
- large template and component libraries
- CMS collaboration and client handoff
- managed hosting, forms, and analytics
- A second metric per experiment, recorded but never used to declare a winner
- A content import from an existing page, mapping its blocks onto your section schemas
- A scheduled publish, which is the same swap with a timer and a check that the build still passes
Need the files? The project pack on the verdict page hands your agent the whole brief · more website builders.