Install: we draw the form#
Paste this where the form should appear. Copy it from the Install tab, We draw the form, so the key is already filled in:
<div data-subcontext-leadform></div>
<script src="https://js.subcontext.com/leadform-latest.js" data-key="lfk_YOUR_KEY" defer></script>
- The
<div>marks where the form goes. You can have more than one on a page. - The
<script>loads the form code and carries your key. Include it once per page. - The form is the one designed on the Design tab.
Change the form for one page#
Add settings to the <div> to override the design on this page only. For example:
<div data-subcontext-leadform
data-fields="first_name, email, phone"
data-submit-label="Call me back"
data-accent="#0a7f3b"
data-utm-source="flyer"
data-value-product-page="life-cover"></div>
Every setting is listed under Options.
Set it up from JavaScript#
For more control (your own fields, words, error messages, or code that runs after each send), start the form from JavaScript:
<div id="enquiry"></div>
<script>
window.addEventListener('subcontext:leadform:ready', (event) => {
const leadform = event.detail // the same as window.Subcontext.leadform
leadform.init({
mode: 'head', // 'head' means "draw the form"
target: '#enquiry',
fields: ['first_name', 'email', 'phone', 'message'],
onSuccess: (result) => console.log('sent', result)
})
})
</script>
<script src="https://js.subcontext.com/leadform-latest.js" data-key="lfk_YOUR_KEY" defer></script>
With a target, init() draws the form there. mode: 'head' does the same. Without a target, init()
makes a form that draws nothing. See Install: your own form.
The script fires the subcontext:leadform:ready event once, as it loads, so add your listener before the
script runs. A defer script runs after the page's other scripts, so the order above is safe. If your
code might run after the form script, use a helper that handles both cases:
function whenLeadform(fn) {
if (window.Subcontext?.leadform) fn(window.Subcontext.leadform)
else window.addEventListener('subcontext:leadform:ready', (e) => fn(e.detail), { once: true })
}
Single-page apps#
The script draws every <div data-subcontext-leadform> on the page when it loads. It also redraws after page
changes in sites built with Turbo. If your site adds the <div> later in some other way (React, Vue, another
router), call this after adding it:
Subcontext.leadform.mountAll()
Each element is drawn only once, so you can call it again.
Install: your own form#
Keep your own HTML form. The script draws only the firm's consent wording and boxes into it, then sends the lead. This is the Install tab's Your own form snippet:
<form id="my-form">
<input name="first_name" autocomplete="given-name">
<input name="email" type="email" autocomplete="email">
<textarea name="message"></textarea>
<div id="my-form-consent"></div>
<button>Send</button>
</form>
<script src="https://js.subcontext.com/leadform-latest.js" data-key="lfk_YOUR_KEY" defer></script>
<script>
window.addEventListener('subcontext:leadform:ready', () => Subcontext.leadform.mountConsent('#my-form-consent'))
document.querySelector('#my-form').addEventListener('submit', async (event) => {
event.preventDefault()
const data = new FormData(event.target)
const values = {}
for (const key of new Set(data.keys())) {
const all = data.getAll(key)
values[key] = all.length > 1 ? all : all[0]
}
const result = await Subcontext.createLead(values)
if (!result.ok) console.log(result.message, result.fields)
})
</script>
Each input's name is the field's key (for example first_name, email, or one of your own like
product_page).
What the script does#
- Loads the form's settings and consent wording as the page loads.
- Checks the values before sending:
- always: an email or a phone number is given, both are in a valid format, and nothing is too long;
- if you list
fieldsininit({ mode: 'headless', fields }): each listed field's required setting, choices, type and checks, as designed.
- Drops answers to designed fields that shouldn't show (their "show if" rule says so).
- Adds where the visitor came from, the consent answers, and which version of the consent wording they saw.
- Solves the built-in bot check, if it is on, before sending.
- Retries once if the connection drops, without ever creating the lead twice.
What it doesn't do#
- Draw anything except the consent block.
- Make the designed form's required fields required. Subcontext doesn't enforce them for your own form either.
Mark them required in your form, or list them in
fields. - Count views and starts for the Results tab.
Subcontext still checks any designed field you send: its choices, type, length and format. For example, sending
topic: "Other" to a drop-down whose options don't include "Other" is refused with invalid.
Reading your form's values#
- The loop in the snippet keeps every value of a repeated name, so a group of checkboxes sends every ticked
box. That is a list, or a single value if only one was ticked. Subcontext accepts both. The shortcut
Object.fromEntries(new FormData(form))keeps only the last one. - A ticked checkbox sends
"on". For a yes/no (checkbox) field,"on","true","yes"and"1"mean yes;"off","false","no"and"0"mean no. An unticked checkbox sends nothing. - The consent block's own inputs (named
consent[...]) are sent as consent answers, not as lead details. So is a Cloudflare Turnstile token (cf-turnstile-response), if you use Turnstile.
From code only, with no form#
const result = await Subcontext.createLead({
first_name: 'Ann',
email: 'ann@example.com',
phone: '07700 900123',
message: 'Please call me about life cover.',
product_page: 'life-cover', // any key of your own
consent: { email: true }, // box keys from consent()
provenance: { utm_source: 'flyer', fbclid: 'abc' }
})
if (!result.ok) showErrors(result.message, result.fields)
To draw your own consent boxes instead of using mountConsent, read the wording with consent(). See
Consent and example page 06.
Same settings for every send#
Subcontext.createLead has no settings of its own. If you want the same settings on every lead (provenance,
hidden values, fields to check, your own messages), make a form with init() and send through it:
const form = Subcontext.leadform.init({
mode: 'headless',
fields: [{ key: 'email', required: true }],
provenance: { utm_source: 'cover-calculator' },
values: { product_page: 'life-cover' }
})
const result = await form.submit({ email: 'ann@example.com' })