PHPackages                             recranet/craft-forms - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. recranet/craft-forms

ActiveCraft-plugin

recranet/craft-forms
====================

Form builder for Craft CMS with submissions, notifications and honest reCAPTCHA handling

v2.7.0(today)022↑2627.3%proprietaryPHPPHP ^8.2

Since Aug 6Pushed todayCompare

[ Source](https://github.com/recranet/craft-forms)[ Packagist](https://packagist.org/packages/recranet/craft-forms)[ RSS](/packages/recranet-craft-forms/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (1)Versions (13)Used By (0)

Recranet Forms
==============

[](#recranet-forms)

Form builder plugin for Craft CMS 5. Built for the Elloro Craft boilerplate as a replacement for `craftcms/contact-form` + `contact-form-extensions`.

**Why it exists:** the stock stack silently marks every submission as spam when reCAPTCHA keys are misconfigured — visitors see success, mails never arrive, nobody notices. This plugin treats captcha *config errors* and *spam* as fundamentally different things.

Features
--------

[](#features)

- **Form builder in the CP** — drag &amp; drop field cards (text, email, tel, textarea, select, checkbox) with per-field width (full/half for side-by-side columns), auto-suggested handles, and per-form notification recipients and subjects. Forms are content (database), so they're editable on production where `allowAdminChanges` is off.
- **Stored submissions** — every submission (including spam-flagged ones) is saved as an element, browsable per form in the CP, searchable, with statuses Sent/Spam/Failed and a per-form reference number (#1, #2, …). Values are keyed by field **uid** with a submit-time **snapshot** of the form definition, so renaming a field never orphans historical data. Mail send failures are recorded on the submission (status Failed) — nothing is ever lost. Identical double submits within 5 minutes are deduped.
- **Multi-provider captcha** — Google reCAPTCHA v2/v3/Enterprise or Cloudflare Turnstile, with honest verdicts:
    - *pass* → submission goes through (v3/Enterprise score persisted on the submission)
    - *spam* (low score, invalid token) → stored + flagged, visitor sees success (no bot tip-off), no email sent. Scores below the **reject threshold** are definite bots: rejected outright, not stored.
    - *error* (bad keys, provider down) → **never treated as spam.** Fail-open (default): submission accepted + flagged with the reason, warning logged, note in the notification email. Fail-closed: visitor sees a real error.
- **Token binding** — v3/Enterprise tokens are checked against the per-form action and the hostname they were minted on, so a token farmed elsewhere cannot be replayed here.
- **Minimum fill time** — submissions arriving faster than a human could type (default 3s, hashed render timestamp) are rejected before any captcha call is made.
- **Sender blocklist** — for human-driven spam a captcha score cannot catch: match a full address, `@domain` suffix, local-part prefix or IP prefix. Matches are stored as reviewable spam.
- **Honeypot** — hidden field, toggleable, configurable name.
- **Storage switches + retention** — mail-only mode (`saveSubmissions` off), drop spam instead of storing it (`saveSpamSubmissions` off), and auto-delete stored submissions after N days (`retentionDays`, runs with Craft's GC or `php craft recranet-forms/gc/prune`). Match retention to the site's privacy statement.
- **Deploy health check** — `php craft recranet-forms/captcha/check` catches missing keys and provider connectivity problems, exits non-zero. Add it to the deploy flow. Note: Google validates tokens before secrets, so a wrong-but-present secret only surfaces at runtime — where it is reported as a config error (visible in the CP and the notification email), not as spam.
- **Email / SMTP test utility** — CP → Utilities → Email / SMTP test verifies the SMTP connection and sends a test mail, surfacing the full transport errors Craft's mailer swallows. Works with `allowAdminChanges` disabled.
- **CSV export** — the submissions index export includes "Submissions (expanded fields)": every form field becomes its own column.
- **Notifications + confirmations** — HTML emails, reply-to set to the submitter, optional confirmation email. Templates overridable per project.
- **Multi-locale** — front-end strings translated for nl/en/de/fr/es/it.

Installation
------------

[](#installation)

```
composer require recranet/craft-forms
php craft plugin/install recranet-forms
```

Set the keys in `.env`:

```
RECAPTCHA_SITE_KEY=...
RECAPTCHA_SECRET_KEY=...

```

Usage
-----

[](#usage)

Render a form anywhere in Twig:

```
{{ craft.recranetForms.render('contact', {
	class: 'my-form',
	buttonLabel: 'button.send'|t,
	redirect: 'contact?submitted=true'
}) }}
```

### Custom form templates

[](#custom-form-templates)

Create `templates/recranet-forms/form.twig` in the project to fully own the markup. The template receives `form`, `options`, `formErrors`, `formContent`, `erroredFormHandle`. Required inputs:

```
{{ csrfInput() }}
{{ actionInput('recranet-forms/submissions/submit') }}
{{ hiddenInput('formHandle', form.handle|hash) }}
{# field inputs as fields[] #}
{{ craft.recranetForms.captchaTag(form.handle) }}
```

`captchaTag()` renders the hashed timestamp field (submit-timing check) plus the configured captcha widget, its token bound to the given action name — give every form its own. The old `recaptchaTag()` still works as a deprecated alias.

Email templates are overridable at `templates/recranet-forms/_emails/notification.twig` and `confirmation.twig`; values come from `submission.values` (submit-time snapshot) or `submission.value('handle')`.

Placing a form in page content
------------------------------

[](#placing-a-form-in-page-content)

Besides the Twig call, forms can be dropped into content: create a field of type **Form** (Settings → Fields), add it to an entry type / Matrix block / CKEditor entry, and pick the form. The field stores the form's **uid**, so renaming its handle never breaks the reference.

```
{{ entry.contactForm }}                      {# renders the picked form #}
{{ entry.contactForm.form.name }}            {# the Form model #}
{{ entry.contactForm.render({ class: 'x' }) }}
```

An empty field renders nothing; so does a field pointing at a form that has since been deleted.

Previewing
----------

[](#previewing)

The form edit screen has preview panels: **Fields → Preview form** renders the front-end template, and the Notification/Confirmation tabs preview the emails with sample values (subject included, merge tags resolved). Previews go through the same template resolution as a real render or send, so "Default template" is something you can look at before overriding it. Nothing is sent or stored.

Settings
--------

[](#settings)

Plugin settings (CP → Settings → Recranet Forms, stored in project config):

SettingDefaultNotesCaptcha providerreCAPTCHA v3none / v2 / v3 / Enterprise / TurnstileSite/secret key`$RECAPTCHA_SITE_KEY` / `$RECAPTCHA_SECRET_KEY`env varsScore threshold0.5below = spam (stored, reviewable)Reject threshold0.3below = definite bot (rejected, not stored)Fail openonwhat happens when verification *itself* errorsVerify token hostnameonrejects tokens minted on other hostnamesHoneypoton, `rf_website`Minimum submit time3s0 disablesSender blocklistemptyaddress / @domain / local-part / IP prefixSave submissionsonoff = mail-only modeSave spam submissionsonoff = flagged spam is droppedRetention (days)0 (keep forever)prunes with Craft GCMulti-site / translations
-------------------------

[](#multi-site--translations)

Editors type form text themselves, so translations are content too: they live in the database, per site, next to the source form — no developer and no deploy to change a wording.

Open a form and pick a site from the **breadcrumb site menu** (the same switcher entries use). The primary site holds the source form; every other site shows a source → translation table for the strings visitors and recipients read: field labels, placeholders, descriptions and consent text, option labels, custom validation messages, the form name, and the notification and confirmation subjects and body text. Leave a translation empty and that string falls back to the source, so a half-translated form still renders completely.

Structure — field types, handles, widths, conditional rules — is deliberately shared with the source form. A translated form stays the same form, so submissions from different sites remain comparable, and option **values** stored with a submission stay in the source language (only their labels translate).

### Translate with AI

[](#translate-with-ai)

With [`recranet/craft-ai-translator`](https://github.com/recranet/craft-ai-translator) installed and configured, each translation site gets a **Translate with AI** button that fills the missing strings using that plugin's provider — so the project's glossary and tone-of-voice settings apply, and merge tags like `{naam}` are left alone. Translations an editor already wrote are never overwritten. Without the plugin the button is simply absent and translating stays manual.

Emails render in the language of the site the submission was made on, whoever triggers the send. The owner notification can be pinned to the primary site's language instead — see Notification language under Settings → Storage. The plugin's own strings (validation messages, buttons, the whole control panel) ship translated for nl/en/de/fr/es/it in `src/translations/`.

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance100

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

12

Last Release

0d ago

Major Versions

v1.0.1 → v2.0.02026-08-06

### Community

Maintainers

![](https://www.gravatar.com/avatar/bdd6d6951efb85c875a00f2ff0e04c4be6bfc2d5943d37cdafd510cd68d3878b?d=identicon)[recranet](/maintainers/recranet)

### Embed Badge

![Health badge](/badges/recranet-craft-forms/health.svg)

```
[![Health](https://phpackages.com/badges/recranet-craft-forms/health.svg)](https://phpackages.com/packages/recranet-craft-forms)
```

###  Alternatives

[spicyweb/craft-neo

A Matrix-like field type with block hierarchy

393818.3k12](/packages/spicyweb-craft-neo)[verbb/formie

The most user-friendly forms plugin for Craft.

101400.6k76](/packages/verbb-formie)[solspace/craft-freeform

The most flexible and user-friendly form building plugin!

54686.7k24](/packages/solspace-craft-freeform)[verbb/hyper

A user-friendly links field for Craft.

24153.5k14](/packages/verbb-hyper)[verbb/vizy

A flexible visual editor field for Craft.

4251.5k1](/packages/verbb-vizy)[verbb/events

A full-featured plugin for event management and ticketing.

2312.1k](/packages/verbb-events)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
