Reveal content when the correct value is entered
This recipe builds a form field that, when the correct value is typed, hides
itself and reveals a block of content — a lightweight “enter the code to
continue” gate. It uses conditional visibility
(show_when) and htmx, with no client-side JavaScript of your own.
This is not authentication. The correct value lives in your S3 config and is compared server-side (it is never sent to the browser), but the endpoint is still guessable by anyone who can POST to it. Treat this as a soft gate for low-stakes content, and pair it with captcha, rate limiting / stage throttling, and the min-fill token if it guards anything you care about. Never use it for real access control.
Prerequisites
- A deployed Webform Relay stack
- The embedding page uses htmx (see the htmx integration notes)
1. Configure the form
Two elements, each with a show_when that references the same code field:
# webform.yaml
forms:
gate:
confirmation: "Thanks!"
fields:
- name: code
type: text
label: Access code
# Visible until the correct value is entered.
show_when: { field: code, not_equals: "open-sesame" }
- type: html
content: |
<div class="unlocked">
<h2>You're in</h2>
<p>Here is the protected content…</p>
</div>
# Revealed only once the correct value is entered.
show_when: { field: code, equals: "open-sesame" }
outputs:
- type: email
to: you@example.comHow it works:
- On first load,
codeis empty, socode’s condition (not_equals "open-sesame") is true → the input shows; thehtmlelement’s condition (equals "open-sesame") is false → it’s hidden. codeis referenced by ashow_when, so it is a controller: the server renders it with htmx attributes that re-fetch the form whenever it changes.- When the visitor types
open-sesameand the field loses focus (achangeevent), htmx re-requests the form. Nowcode’s condition is false, so the input collapses to a hidden input that carries the value — keeping the reveal stable — and thehtmlelement’s condition is true, so the content appears.
Because the collapsed code still submits its value, the state survives a
form submission, and validation skips the (now hidden) field.
Upload the config:
task relay:upload:config CONFIG=webform.yaml2. Embed the form with htmx
Load the server-rendered form (endpoint A) and let it re-render itself in place:
<script src="https://unpkg.com/htmx.org@2"></script>
<div hx-get="https://<api-id>.execute-api.<region>.amazonaws.com/api/v1/form/gate"
hx-trigger="load"
hx-swap="innerHTML"></div>hx-trigger="load"fetches the initial form when the page loads.- The relay emits the controller’s re-render attributes (
hx-getback to/api/v1/form/gate,hx-trigger="change",hx-target="closest form",hx-swap="outerHTML",hx-include="closest form"), so each change tocodere-renders the whole form with the current values preserved.
If your form is on a different origin than the relay, make sure the relay’s
AllowedOrigins includes your site (see
CORS) — the htmx request headers are already
on the allowlist.
3. Try it
Load the page: you see the Access code field. Type open-sesame and tab
out — the field disappears and the protected content appears. Enter anything
else and the field stays, repopulated with what you typed.
Notes and limits
- Server-side secret.
open-sesamenever leaves the server; the browser only ever receives the rendered form. But a determined visitor can brute-force the endpoint — see the not-authentication warning above. - One-way reveal. Once
codecollapses to a hidden input it no longer fireschange, so the reveal is final for that page load. Reloading starts over. - Multiple gates / richer conditions.
show_whensupportsall/anygrouping and theone_of/filled/emptyoperators — see the field model reference.