WP Manifestindependent plugin directory
manifest / forms / lonsda-light-form

Lonsda Light Form releases

Lightweight Carbon Fields form builder plugin for wordpress

by Aivars Lauzis · github.com/lauzis/lonsda-light-form · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/lauzis/lonsda-light-form/archive/refs/heads/main.zip

Readme

lonsda-light-form

Lightweight Carbon Fields form builder for WordPress.

Built on lauzis/wp-plugin-packages, so its settings page, logging, notices, toasts and migrations are the same components the other plugins in this account use.

Why it exists

Our site ran on Gravity Forms, which is an excellent plugin. Nothing here is a complaint about it.

When its licence came up for renewal we looked at what we were actually using, and it was a very small part of what it offers: receive a contact form, store what was submitted, send an email about it. Conditional logic, multi-page forms, payment gateways, calculations, the integrations directory — all of it real, well built, and none of it in use on our site. Renewing would have paid for a great deal we had never touched.

So this does the part we needed. It is not a replacement for Gravity Forms and should not be chosen over it by anyone using more than a fraction of it — the comparison below is there to make that easy to check. What it is instead is small: one person can read the whole of it, there is no licence to renew, and nothing in it is trying to sell you the paid version.

Against Gravity Forms

Lonsda Gravity Forms
Field types text, textarea, checkbox 30-odd, including file upload, date, address, payment
Validation required, email, regex, max length the above plus per-field rules and custom validators
Conditional logic fields, pages and notifications
Multi-page forms yes
Entries stored, listed, filterable, CSV the above plus notes, editing, bulk actions, partial entries
Notifications one per form, per-form recipients many per form, routed by conditional logic
Auto reply yes yes
Spam honeypot, minimum completion time, reCAPTCHA v2 the above plus Akismet and reCAPTCHA v3
Translations keys generated per field, .po/.mo or WPML WPML/Polylang integration
Import/export JSON JSON
Integrations a hook Mailchimp, Stripe, HubSpot, Zapier and many more
Licence none annual

The right-hand column is why Gravity Forms costs what it does, and it is worth the money to anyone using it. If you need any of that, buy it — this is not a substitute. The point of the table is the left-hand column: for our project, that narrow list was the whole requirement.

What replaced the integrations is one action. A submission is handed to lonsda_form_submitted and the theme decides what it means — here, adding the address to the right Mailchimp-style audience for the language it was submitted in. That is about thirty lines, and it is exactly as much integration as the site needed.

What it does

  • Form builder — fields with labels, placeholders, defaults and validation, edited in a tabbed screen and collapsed to a readable list.
  • Three field types — single-line text, text area, checkbox.
  • Validation — required, email, custom regular expression, maximum length. Enforced server-side, not only in the browser.
  • Block and shortcode — the same form rendered either way, at view time rather than frozen into post content.
  • Confirmation — per-form wording in a visual editor, and whether the form is hidden once accepted.
  • Notifications — per-form recipients, subject and message, with any field usable as {field_name}.
  • Entries — every submission stored, listed, filterable, marked New until opened, exportable as CSV.
  • Translations — labels and buttons carry keys, translated in the browser or through .po/.mo files, with WPML supported directly.
  • Spam — honeypot, minimum completion time, and optional per-form reCAPTCHA v2 with a test on the settings page.
  • Import and export — form definitions as JSON, all or a selection.
  • Self tests — ten scenarios, around 145 assertions, run against the live install and cleaning up after themselves.
  • Hooks — every submission is handed on with consistent metadata, so a theme can do anything this does not.

Building a form

A form is a title and a list of fields, created under Lonsda Forms → Add Form. Each row in the Fields list becomes one input.

Label Shown above the input. Required — a row without one is discarded on save.
Name Identifier used when the submission is handled. Derived from the label if left blank. Duplicates get a numeric suffix, because two fields sharing a name would silently overwrite each other.
Type text, textarea or checkbox.
Placeholder Hint inside the empty input. The label stays visible regardless — a placeholder vanishes as soon as someone types, so it is a poor label. Not offered for a checkbox.
Ticked by default Checkbox only. Leave off for anything the visitor should actively agree to.
Required Rejected when empty, or unticked for a checkbox.
Validation None, email, or a custom pattern. Only for single-line text — an email check on a textarea would never be useful, and is cleared if the type changes afterwards.
Pattern A regular expression the value must match, written without delimiters, e.g. [A-Z]{2}[0-9]{4}. Shown only when Validation is set to a pattern.
Maximum length In characters, not bytes. Blank or zero means no limit.

Validation runs again on the server. What the renderer emits — required, maxlength, type="email", pattern — is a convenience for the visitor, and anyone can remove it before posting.

Editing a form

The editor is tabbed, because the field list is what gets worked on repeatedly and everything else was pushing it off the screen.

Tab What is on it
Fields The inputs, collapsed to a list of labels so a long form stays readable.
Submit button Button wording and its translation key.
Confirmation The message shown after a submission, and whether the form is hidden.
Notifications Who gets emailed, and whether entries are kept.
Protection reCAPTCHA, when it is configured.

Putting a form on a page

Either the Lonsda Form block, or the shortcode:

[lonsda_form id="1"]

The id is the one shown in the Forms list. Both render at view time rather than freezing a copy into post content, so editing a form updates it everywhere.

Two ids, one of which is the right one

A form is edited as a post and rendered from a row in its own table, so it has a post id and a form id. The Forms list, the shortcode and the block all use the form id; the post id only ever appears in the address bar while editing.

They are easy to confuse and the failure is silent — a lookup by the wrong id simply finds nothing. So a lookup that fails checks whether the id given was a form's post id, and if it was, the message names the id that should have been used. That message is shown to administrators only; visitors get nothing rather than an error printed into the page.

The block reads the form id from an llf_id field on the REST response for the same reason. The editor lists forms over the post REST API, which knows only post ids, so without it the block would hand a post id to a lookup keyed by the form id.

After a submission

Each form carries its own confirmation wording, edited in the visual editor, and a Hide the form after submission setting that is on by default — leaving a filled-in form under a "thank you" reads as though nothing was sent and invites a second submission. Switch it off to leave the form in place.

A form saved before these settings existed has neither stored. Both fall back to the shipped default, so the behaviour is the same as if the defaults had been chosen deliberately.

A submission that was not accepted

Whatever the reason, the form comes back filled in. Nobody retypes anything: a failed validation redisplays every answer and marks only the fields at fault, a tripped spam check hands the whole lot back, and so does a form that had been open too long for its nonce to still verify — the case where the loss would hurt most, since it is nobody's mistake and can swallow a message somebody spent ten minutes on. That last one redisplays with a fresh nonce, so sending again works rather than failing the same way.

The answers are read and sanitised before the nonce is checked, which is what makes the expired case possible. They are unverified at that point, so they are sanitised by field type exactly as an accepted submission's are, kept only for fields the form actually has, and escaped again by the renderer on the way out — a form to press send on, not content the page has taken on trust. Nothing is stored and no hook fires: a submission that failed the nonce or a spam check still reaches neither lonsda_form_submitted nor lonsda_form_rejected.

A submission that succeeded is the one case the answers are dropped. Leaving them under a "thank you" reads as though nothing was sent.

Notifications

A new form arrives prefilled: recipients from the site administration address (Settings → General, the only contact address WordPress itself keeps) and the subject as {form_title}.

Prefilled, not assumed. The address sits in the box where it can be read and changed before the form is saved, rather than a form quietly mailing an address nobody chose. Clearing the field sends nothing.

  • Several addresses, separated by commas. Anything that is not an address is dropped rather than handed to wp_mail(), where one bad entry can lose the whole send.

  • Both the subject and the message accept placeholders. Any field can be used by its Name — a field named surname becomes {surname}. That is the Name, not the Label. A blank answer comes out as nothing; a checkbox as Yes or No.

  • {all_fields} expands to every field and its answer, in form order: the label in bold, the answer on the line beneath, a blank line before the next. Leave the message empty and that is what you get, wrapped in the metadata below.

  • Notifications are sent as HTML, so a label can be bold and an answer can sit under it rather than beside it. The markup is spare on purpose — paragraphs, a rule, small text — because heavy markup is what gets a message held back as suspicious. A message you write yourself goes through wpautop, so the line breaks you typed into the box survive. The form editor lists them all in a Placeholders panel under Publish, including this form's own field tokens by name and label — which is the half that cannot be documented, since it depends on the form. Click one to copy it.

  • {submission_details} is the small block of where and when it came from — page, language, IP, timestamp — with any of those it does not have left out. That conditional part is a placeholder rather than code so the default message can be a single string, which is what makes it translatable.

  • Also available: {form_title}, {site_name}, {site_url}, {submitted_at}, {page_title}, {page_url}, {language}, {locale}, {ip}, {user_agent}. A field whose Name matches one of these does not displace it — the fixed set has to mean the same thing on every form.

  • The subject default is {form_title} rather than the title itself because a field default is fixed when the field is registered and has no form to ask. It also keeps up with a form that is later renamed. An empty subject falls back to New submission: <form title>.

  • Naming a field in Reply-To field makes replies go to whoever submitted it.

  • Everything a visitor typed is escaped on the way in. It is the one place a stranger's words are placed into markup that lands in someone's inbox.

Because a test form is now a form that would email the administrator, the self tests cancel every send at a priority nothing else uses. A full run makes zero wp_mail() calls.

Both sending and storing are listeners on lonsda_form_submitted, with no more access than a theme doing the same job. lonsda_form_notification filters the mail before it goes; returning an empty array sends nothing.

Read the full README on GitHub →

Releases

TagPublished
v0.29.1 Aug 18, 2026
v0.29.0 Aug 18, 2026

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.