Nibble 0.22.0
SecurityForms that calculate, hold and report. A public form can total a quote, open and close itself, catch spam and notify the right people, and its submissions are handled in the Control Plane like any other work.
This release is mostly forms and fields, and a plainer Control Plane. Four database migrations are added; they only add tables and columns, so they can be rolled back, but the snapshot taken before an upgrade is the surest way back.
What’s new
- Public forms can be handled, not just stored. Each submission is New or Handled, can be marked unread, and carries internal notes, and the list takes bulk actions. A person’s submissions can be exported or erased by email address, exports and deletes are recorded in the audit log, and the submissions of a form removed from the schema are purged after the retention period.
- Form notifications need no code.
notifyentries merge{field.x}and{global.set.field}into the subject and message, take recipients from a field or a global (to: {field.email}), and are routed withif:. Each email is recorded on the submission, and Send notifications again resends them. - A form can open, close and cap itself.
limits:takesopens_at,closes_at,max_submissionsandunique(a value accepted once, such as a ticket code), with aclosed_messageshown when it is closed. A form’ssubmit_labelreplaces the button text. - More ways to catch spam.
spam.min_secondstreats a submission sent too soon after the form was shown as spam, andspam.max_linkscatches text full of links. The rate limit now counts only accepted submissions, so refused posts no longer lock a real visitor out, and the refusal says how long to wait. - A form field can be encrypted.
encrypt: truestores the value encrypted, bound to its submission and field. Staff, exports, emails, deliveries and per-person search still read it. - Multi-step forms, saved forms and calculations.
steps: trueturns sections into checked steps.resume: trueadds Save and finish later, with a link emailed to the visitor. Acalculationfield is worked out on the server when the form is sent, with a live figure in the theme throughcalculated(). - Forms speak the visitor’s language. A multilingual site translates a form’s text, Nibble’s own form messages and the visitor’s confirmation email.
- Hidden and prefilled form fields. A
hiddenfield is filled from a fixedvalueor from the page’s query string withprefill. - Themes get what they need to draw a form. A form field arrives with its fieldtype settings, its resolved default
and its conditions as rules to evaluate.
useNibbleFormapplies conditions, leaves out fields hidden by one, refills typed values after a refused post even without JavaScript, and gives ids,aria-describedby, an error list and focus targets. Crumbs’ contact form draws every public fieldtype accessibly, with steps, calculations and the closed message. - Rich text can link to a global. Choose a global field in the link dialog, or write
[Apply](global:site.apply_url)through the API, and the link follows the global’s address when the text renders. - Content migrations work on globals.
rename_fieldandset_defaulttake aglobalin every locale, and the newmove_fieldandremove_fieldcarry values to another global or drop a field that left the schema, so nothing is stranded. - New fieldtypes.
decimal,range,rating,time,colorandbutton_groupwork in blueprints and public forms, with Control Plane controls and crumbs inputs.noteshows Markdown between a form’s fields and stores nothing. Decimal, range and rating can be used in calculations. - Date-times can have a time zone.
time_zone: Europe/London(orsite) reads the time as typed in that zone, stores the moment in UTC and shows it in that zone. A time the clocks skip is refused, and a repeated one takes its first occurrence. - Text can be validated by what it holds.
input_type: email,urlortelchecks the value on save, in blueprints and on public forms. A toggle’sinline_labelcan hold Markdown links, for a consent checkbox. - A field’s
requiredandread_onlycan take a condition. The required marker shows only while the condition passes, and a hidden field keeps its stored value. - Read-only means read-only. A value sent for a
read_onlyfield is kept as stored whoever saves, rows in grids and replicators by theirid. Entry, term, global and asset writes through the API answer with anignoredlist of the fields whose value was kept. - Apps see fuller field schemas. The JSON Schema an app writes against describes length, format, range, step, option labels, read-only state, conditions and when a field is required.
list_form_submissionstakessinceandq, searching as the Control Plane listing does.site/types.d.tstypes each form’s values, and a view’s form prop uses them, sovalues.topicis typed.useNibbleFormtakes that type; an untyped call is unchanged.computed:todayis a built-in default, the current date in the site’s time zone.- Blocks are easier to work with. Undo a removed block or grid row from the notice that appears, move blocks and
rows up and down from their menu, copy a block and paste it into any field with the same set, and use
collapse: accordionto keep one block open at a time. Grids and blocks have a Full screen action. - Rich text options now do what the reference says.
placeholder,character_limit(with a counter),toolbar_mode,smart_typography, input and paste rules,link_noopener,link_noreferrer,link_collections(the link dialog’s Page tab lists routed collections),always_show_set_button,previewsandinline_hard_breaks. - Pickers have modes and can create.
entriesandtermsfields takemode: auto | default | select | typeahead | stack;auto, the default, is a dropdown for 25 or fewer choices. People allowed to create the item see Create a new … in the picker.nibble:checkrefuses an unknown mode. - Duplicate an entry from the editor or the listing. A field’s
duplicateoption decides what is copied; slug anduniquefields are left out by default. - Entry listings sort and filter by field. Field columns sort, and listed choice, toggle and date fields add
filters.
sortableandfilterableturn them off or on per field. - Documentation covers more of the site. A theming guide to forms, pages on what a view receives and on styling and the page shell, a Reference section listing every fieldtype option, validation rule and condition, and the forms guide split into declaring, delivery, spam and privacy, translation and a key reference. Tests fail when the code gains an option the pages miss.
- Guides for the people who run a site. Webhooks, the Utilities screens, every
nibble:checkmessage, every environment variable and task, the page cache and CDN, sign-in lockout and recovery, and administering from the Control Plane without a terminal. bin/rails nibble:dev:setupasks which AI tools to set up — Claude Code, Codex or Cursor, those found on this computer chosen by default — and writes only into the site’s folder.--tools claude,codexanswers without asking. Codex gets a project.codex/config.tomland the skills, like Claude Code.- The Connected apps screen shows
nibble connect <site>, which sets up Claude Code, Codex and Cursor in the folder it runs in, with the site’s MCP server and skill. It needs thenibblecommand 0.3.0 or later. - Apps are told more about each field. A field’s description says when it is shown, required or read-only, and the bundled schema, theming and extending skills cover the new fieldtypes, forms and reference pages.
- Guides for editors, and routes in. Settings, categories and tags, the trash, your account and two-factor sign-in, and form submissions, with start-here routes by audience and every guide listed on the docs home.
What’s fixed
- Crumbs ships a fallback
defaultview, andnibble:checkwarns when a collection’s entries resolve to a view the theme lacks. A set without a view is a warning, since its blocks render nothing. - The 500 page gets the site’s shared props, falling back to empty ones if loading them fails.
- Saving changes to a live entry no longer warns that someone published a newer version. The warning now appears only when the entry really was published after those changes were started.
- Publish works on a new entry, creating and publishing it in one click, and a new entry in a collection that needs review offers Submit for review.
- Apps are no longer told a field hidden by its condition isn’t saved; it keeps what’s sent, and only its rules are skipped.
- The Backups screen links to the restore section of the backups guide.
- The health check asks the page renderer on the configured port.
nibble:generate:formwritesrequired: true.- Form emails show every fieldtype’s value again. Only a fieldtype with its own display formats a submitted value.
- A form field’s
messagesoverride the step error, and a calculation’s posted value is never kept in a saved form.
Changed
- Plainer wording in the Control Plane. Page-builder sets are blocks, globals are Site settings, pending edits to a live page are a Draft, a taxonomy shows its own name, and link targets are filtered by Type. Statuses show their label (In review, not the stored key), error summaries name a field once by its label, and messages say what to do next in plain words.
- History, the audit log and approvals say what happened. They show field labels, readable values, titles and verbs, and an approval says what it does and changes. Signed-in devices have names.
- The Control Plane formats numbers and dates in the site’s locale, the default one in
config/nibble.yml. When that locale names only a language, such asen, the browser’s region for it decides the style, so an Australian browser sees day/month/year and an American one month/day/year; a site that declares no locale follows the browser. Until now every Control Plane wrote Australian dates. - Default validation messages name the field once: “Your name is required.” rather than “The Your name field is
required.”. Their keys are unchanged, so a site’s own
messages:and translations still apply. - Date fields are compact unless
full_width: true, or the date-time has atime_zone. - Relationship fields with 25 or fewer choices show a dropdown and offer Create a new ….
nibble:checkreports more: records holding a switched-off required toggle, saved text failing an email, URL or phone check, a picker mode it doesn’t know, and alink_collectionshandle that names no collection.
Security
- CSV exports can’t run formulas. A cell that starts like a spreadsheet formula is written as text, in entry, term, submission and per-person exports.
- Rich text links to other sites carry
rel="noopener"by default. - A resume link’s token stays out of the request log, and the personal data export of saved forms leaves it out.
Upgrade:
- A required toggle must be switched on, everywhere.
required: trueon atogglenow means it has to be on: entries holding one switched off can’t be published, and terms and globals can’t be saved.bin/rails nibble:checklists them; fix them, or droprequiredif “off” is a valid answer. - CSV exports put
'before cells that look like formulas. A script that parses an export sees'=SUM(A1)where it saw=SUM(A1); strip a leading'if you read the files. - Writes to a
read_onlyfield are now ignored from the Control Plane, API tokens and MCP, and likewise forvisibility: read_only. An integration that sets such a field must use an import, a content migration or a scheduled task, which are exempt, or you removeread_onlyfrom the field. - Email, URL and phone
textfields are checked on save. A saved record whose value fails can’t be saved until it is fixed;bin/rails nibble:checklists them. To check a phone number against your own pattern, usevalidate: regex:, which replaces the phone check. - A rich text field with
character_limitrefuses longer text on save. Content already over a newly set limit can’t be saved until it is shortened. to: {global…}on a form notification lets whoever edits that global choose where submissions go. Keep such a global to people you’d trust with the inbox.- A custom theme must post
_nibble_t(a hidden input holdingform.timestamp) when a form setsspam.min_seconds, or every submission is caught as spam.useNibbleFormand crumbs already do. limits.uniquecovers only submissions accepted after it’s added. Duplicates already stored are left alone.- Rich text links to other sites now get
rel="noopener". Setlink_noopener: falseon the field to keep the old output. - Editors will notice: date fields are compact unless
full_width: trueor zoned; grids and blocks have a Full screen action; relationship fields with 25 or fewer choices show a dropdown and offer Create a new … to people allowed to; entry listings gain filters for listed choice, toggle and date fields; and the new names (blocks, Site settings, Draft, Type). - Four new database migrations add form handling (handled state, notes, retention), encrypted submission fields,
submission keys for
limits.uniqueand saved forms (form_partials).bin/rails nibble:upgraderuns them. They only add tables and columns, sobin/rails db:rollbackcan undo them, which drops what they hold. site/types.d.tsgains form value types.bin/rails nibble:upgraderegenerates it; commit the new file.- AI apps you connected with
nibble0.2.0 were set up for your whole account, so a site’s tools and guide load in every project. Updatenibbleto 0.3.0, runnibble disconnect <site>once, thennibble connect <site>in the folder where you work on that site. If you rancodex mcp add nibble-dev, remove it withcodex mcp remove nibble-devand runbin/rails nibble:dev:setupagain to choose your tools.