Skip to content

Editable content and forms

Let anyone in the workspace change words and pictures without a deploy, and collect form entries in Studio.

Declare content and forms

A site says which words and pictures people may edit, and which forms it takes, in a benchy.site.json file at the top of its output folder. Benchy reads it on each deploy. Content is grouped in collections: a single set of fields, such as the shop’s details, or a list of items with the same fields, such as prints.

benchy.site.json
{
  "version": 1,
  "content": {
    "prints": {
      "label": "Prints",
      "list": true,
      "fields": {
        "title": { "type": "text", "label": "Name", "required": true },
        "price": { "type": "number", "label": "Price" },
        "photo": { "type": "image", "label": "Photo" },
        "inStock": { "type": "boolean", "label": "In stock" }
      }
    },
    "shop": {
      "label": "Shop details",
      "fields": {
        "hours": { "type": "text", "label": "Opening hours" },
        "about": { "type": "longtext", "label": "About us" }
      }
    }
  },
  "forms": {
    "order": {
      "label": "Order requests",
      "fields": {
        "name": { "type": "text", "required": true },
        "email": { "type": "email", "required": true },
        "print": { "type": "select", "options": ["Benchy", "Planter", "Lamp"] },
        "message": { "type": "longtext" }
      },
      "redirect": "/thanks/"
    }
  }
}
  • Benchy looks for the file only at the top of the output folder, next to index.html, never at the project’s root.
  • "version": 1 is optional; it names the format this page describes.
  • Names of collections, forms and fields use letters, digits, - and _, and are case-sensitive: inStock and instock are two fields. id is reserved, because every served list item carries its own id. Each label is what Studio shows.
  • A new field starts empty. A field you remove is hidden and its values kept, so rolling back to a version that had it brings them back.
  • A deploy without benchy.site.json serves no content: its /_benchy/content/ addresses answer 404 until a deploy that has the file. Nothing is deleted, so the content comes back with it.
  • A deploy with an invalid benchy.site.json is refused, and the error names the setting, such as content.prints.fields.photo.type. Only a JSON syntax error gives a line number.
Content limitValue
Collections per website20
Forms per website20
Fields in a collection40
Options in a select field50
A label80 characters
Items in a list1,000
One item16 KB
Links in one item, each checked by Web Risk50
One collection’s JSON4 MB
All content on a website16 MB, pictures not included

Field types

TypeHoldsUsed in
textOne line, up to 500 charactersContent and forms
longtextPlain paragraphs, up to 10,000 charactersContent and forms
numberA numberContent and forms
booleanYes or no; a checkbox in a formContent and forms
dateA calendar dateContent and forms
emailAn email addressContent and forms
urlA web addressContent and forms
selectOne of the listed optionsContent and forms
imageA picture with its alt text, up to 25 MBContent only

A date is stored and served as YYYY-MM-DD, such as 2026-10-03.

Edit content in Studio

On the website’s page in Studio, Content shows one form per collection. Anyone with Can edit changes the words, adds, reorders and removes items, and uploads pictures, with no agent and no deploy. After Save, visitors get the change within 15 seconds, usually sooner.

  • Content isn’t part of a version: rolling back the site keeps today’s content.
  • Content text counts only against the website’s own 16 MB content limit, not the workspace’s storage. Pictures uploaded for content count toward the workspace’s storage.
  • Each save is in the website’s history, with who made it.

Use content in your pages

Each collection is served on your own site at /_benchy/content/<collection>.json. A list collection is { "items": [...] } in the order set in Studio; a single one is an object of its fields.

JavaScript
const response = await fetch("/_benchy/content/prints.json");
const { items } = await response.json();
// items: [{ "id": "…", "title": "3DBenchy", "price": 12,
//           "photo": { "url": "/_benchy/content/files/…", "width": 1200, "height": 900, "alt": "…" },
//           "inStock": true }]

A preview reads the live site’s content, shaped by the preview’s own benchy.site.json: a field the preview adds shows empty, and one it removes isn’t served. Pages that must show content without JavaScript can be rebuilt from it instead: pull it, build, and deploy.

Content from agents and the CLI

benchy site content
benchy site content pull olive-prints   # writes content/prints.json and content/shop.json
benchy site content push olive-prints   # saves your edited files as the site's content

Agents use site_content_get and site_content_set. Both need a key or app that can make changes in the workspace; a key that only publishes websites reads content but can’t change it.

Add a form

Declare the form in benchy.site.json, then post an ordinary HTML form to /_benchy/forms/<name> and include Benchy’s form script:

HTML
<form method="post" action="/_benchy/forms/order">
  <label>Name <input name="name" required></label>
  <label>Email <input name="email" type="email" required></label>
  <label>Message <textarea name="message"></textarea></label>
  <button>Send</button>
</form>
<script src="/_benchy/forms.js" defer></script>
  • The script adds a small spam check, served from forms.benchy.site, so it works the same on a custom domain, and sets no cookies on your site. It also adds a hidden trap field. A form without the script is refused.
  • After a good entry the visitor goes to the form’s redirect page, or back to the same page with ?sent=order when there is none.
  • A form sent with fetch and Accept: application/json gets { "ok": true }, or "ok": false with the fields that need fixing.

Read entries

Forms on the website’s page lists each form’s entries, newest first, with the page it was sent from. Owners and members with Can edit see them; Can view doesn’t, because entries hold people’s details.

  • Export CSV downloads every entry, or those in a date range.
  • Delete an entry at any time. Entries are deleted automatically after 90 days.
  • Entries count toward the workspace’s monthly quota. See Plans.
benchy site forms
benchy site forms export olive-prints order > orders.csv
benchy site forms export olive-prints order --since 2026-09-01 > september.csv

Agents use site_forms_list and site_forms_export. Tell your visitors what you collect and why, on the page with the form.

Email notifications

Each member chooses Email me new entries on a form for themselves. The email is plain text, from forms@noreply.benchy.studio, with the form’s fields and the site’s address. Replying goes to the address in the entry’s email field, when it has one. A website sends at most 20 of these emails an hour; after that, one summary an hour lists the rest.

What forms accept

  • Declared fields only, checked against their types and required; anything else sent with the form is dropped. An entry is at most 32 KB of text, with no file uploads.
  • A form can’t ask for a password, payment details or a government ID number. A deploy that declares a password field, or fields for a card number, expiry date, security code or government ID number, is refused.
  • One visitor can send 10 entries a minute to a site. Entries that fail the spam check are dropped and don’t count toward the quota.
  • When a Free workspace reaches its monthly quota, its forms answer “This form isn’t taking entries right now” until the next month.

Use ← and → to move between pages.