Crear bloques de formulario personalizados en páginas de destino
Los bloques de formulario de las páginas de destino de Braze capturan entradas estándar, como campos de texto, casillas de verificación y menús desplegables. Los bloques de formulario personalizados amplían las posibilidades al permitirte crear tus propios elementos interactivos, como una calificación con estrellas, un SELECTOR de sentimiento con emojis o una tarjeta de rasca y gana.
Cuando un visitante envía el formulario personalizado, el valor que seleccionó se valida y guarda junto con tus campos estándar, y luego se envía a Braze como un atributo de usuario personalizado. Esto te permite recopilar datos más ricos y atractivos sin salir del editor de páginas de destino.
Los bloques de formulario personalizados se crean con un único helper de JavaScript, window.brazeHelpers.forms.registerFormInput, al que llamas desde un bloque de Custom Code en la página de destino.

Los cuestionarios y los mensajes dentro de la aplicación tienen sus propios bloques de formulario, pero registerFormInput —la API de JavaScript para conectar una interfaz codificada de forma personalizada a un bloque de formulario— solo está disponible en las páginas de destino.
Cómo funciona
Un campo de formulario personalizado es cualquier elemento en tu página de destino cuyo valor quieras capturar y enviar con el formulario. Conectas ese elemento al sistema de formularios de Braze registrándolo. El registro le indica a Braze qué elemento observar, cómo leer su valor actual y qué hacer con ese valor cuando se envíe el formulario.
- Crea tu interfaz personalizada dentro de un bloque Custom Code en la página de destino y asígnale un selector CSS estable, como un
id. - Registra el elemento llamando a
window.brazeHelpers.forms.registerFormInputcon un objeto de configuración. - Braze llama a tu función
getValuepara leer el valor actual cuando lo necesita. - Si el campo es obligatorio, o proporcionas una función
onValidate, Braze bloquea el envío hasta que el valor sea válido, y marca un elemento no válido con una clase CSS que puedes estilizar. Consulta Validación y campos obligatorios. - Cuando el formulario se envía y la validación es exitosa, Braze llama a tu función
onSubmit, donde puedes llamar al SDK de Braze para registrar información como un atributo personalizado de usuario.
Dado que tú proporcionas las funciones que leen, validan y envían el valor, este enfoque funciona con prácticamente cualquier elemento de formulario personalizado, por lo que no estás limitado a los tipos de campo estándar del editor.
Marco básico
El registro más sencillo apunta a un elemento, lo trata como obligatorio, lee su valor a partir de un atributo de datos y escribe ese valor en un atributo de usuario personalizado cuando se envía el formulario. Envuelve la llamada en un listener DOMContentLoaded, como en los ejemplos de esta página, para que el elemento exista antes de que se ejecute registerFormInput:
document.addEventListener("DOMContentLoaded", () => {
window.brazeHelpers.forms.registerFormInput({
selector: "#my-custom-input",
isRequired: true,
getValue: (element) => element.dataset.value ?? null,
onSubmit: (value) => {
window.brazeBridge.getUser().setCustomUserAttribute("my_attribute", value);
},
});
});
Llama a registerFormInput una vez por cada entrada personalizada en la página. Los campos de formulario estándar colocados a través del editor de páginas de destino no necesitan registrarse; el registro solo es necesario para las entradas personalizadas que construyes en un bloque de Custom Code.
Referencia de configuración
registerFormInput acepta un único objeto de configuración. Expresado como firma de función, la forma completa es:
window.brazeHelpers.forms.registerFormInput({
// Provide exactly one of `selector` or `element` to identify the input.
selector?: string,
element?: HTMLElement,
isRequired?: boolean | Promise<boolean>,
getValue: (element: HTMLElement) => value,
onValidate?: (value, element: HTMLElement) => boolean | Promise<boolean>,
onSubmit?: (value, element: HTMLElement) => void | Promise<void>,
});
Como mínimo, debes proporcionar una forma de localizar el elemento (selector o element) y una función getValue. Todo lo demás es opcional.
| Propiedad | Tipo | Obligatorio | Descripción |
|---|---|---|---|
selector |
string |
Sí (o element) |
Un selector CSS que coincida con tu elemento personalizado, por ejemplo "#scratch-card". Braze lo resuelve de forma diferida con querySelector en el momento de la validación y el envío, por lo que puede coincidir con un elemento añadido al DOM después de que se ejecute registerFormInput. |
element |
HTMLElement |
Sí (o selector) |
Una referencia directa al elemento, utilizada en lugar de selector. Solo se usa mientras el elemento permanece conectado a la página y tiene prioridad sobre selector cuando se proporcionan ambos. |
isRequired |
boolean \| Promise<boolean> |
No | Cuando es true, el formulario no se puede enviar hasta que la entrada tenga un valor no vacío: null, undefined, cadenas vacías (incluidas las que solo contienen espacios en blanco) y arrays vacíos se consideran vacíos. También puede ser una promesa que se resuelve en un booleano, que Braze reevalúa cada vez que se valida la entrada, de modo que puedes decidir el estado requerido en tiempo de ejecución. El valor predeterminado es false. Consulta Validación y campos obligatorios para conocer el orden completo de validación. |
getValue |
function |
Sí | Devuelve el valor actual de la entrada. Braze pasa el elemento coincidente como argumento, de modo que puedes leer el valor del DOM, por ejemplo element.dataset.sentiment, o de una variable en tu propio código. Devuelve null cuando aún no hay valor. |
onValidate |
function |
No | Recibe el valor actual y el elemento coincidente, y debe devolver boolean \| Promise<boolean> —el mismo tipo de retorno que isRequired— donde true significa que el valor es válido y false significa que no lo es. Úsalo para aplicar reglas más allá de tener un valor, por ejemplo que el valor sea uno de un conjunto permitido. Si se omite, solo se aplican isRequired y la validación nativa de restricciones. |
onSubmit |
function |
No | Se ejecuta cuando se envía el formulario y la validación se aprueba. Recibe el valor actual y el elemento coincidente. Aquí es donde registras el valor en Braze, normalmente con setCustomUserAttribute. |
Actuar sobre el valor en onSubmit
onSubmit es una devolución de llamada de JavaScript simple, por lo que puedes actuar sobre el valor capturado de la forma que tu integración necesite. Dado que onSubmit se ejecuta como parte del envío del formulario, las llamadas a brazeBridge realizadas dentro de él funcionan como se espera, incluso para un visitante que abrió la página de destino de forma anónima. Consulta Disponibilidad del puente para conocer la otra situación en la que las llamadas al puente funcionan.
El patrón más común es escribir el valor capturado en el perfil de usuario con el puente JavaScript de Braze disponible en las páginas de destino:
window.brazeBridge.getUser().setCustomUserAttribute("attribute_name", value);
Usa un nombre de atributo personalizado que ya exista, o uno que quieras crear, en tu espacio de trabajo. El valor que pases se almacena en el perfil del usuario y luego se puede usar para segmentación, personalización y para desencadenar mensajes de seguimiento.
No estás limitado a atributos personalizados. Desde la misma devolución de llamada, puedes invocar cualquier método de brazeBridge.getUser(). Por ejemplo, para añadir al usuario a un grupo de suscripción, establecer un atributo estándar, registrar un evento personalizado o enviar el valor a tu propio endpoint de API.

No necesitas llamar a requestImmediateDataFlush dentro de onSubmit. El proceso de envío del formulario vacía automáticamente todos los datos a Braze después de que se complete tu devolución de llamada onSubmit.
Ejemplos
Los siguientes ejemplos son completos e independientes. Cada uno es un único bloque que contiene marcado, una etiqueta <script> y una etiqueta <style> que pegas en un bloque de Código personalizado (HTML) en tu página de destino. La llamada registerFormInput cerca del final de cada script conecta la entrada personalizada con el formulario de Braze. Pasa el cursor sobre un ejemplo de código y selecciona el icono de copiar para copiarlo.

Estos ejemplos se ejecutan completamente en el navegador del visitante. En el ejemplo de raspa y gana, el “premio” lo elige JavaScript en el navegador del visitante, por lo que un visitante con conocimientos técnicos podría modificar ese código para obtener el resultado que desee. No confíes en este patrón para recompensas, descuentos u otros resultados que requieran una aplicación estricta por visitante. En su lugar, valida todo lo que sea sensible en términos de seguridad o ingresos en tus propios servidores.
Objetivo: El visitante selecciona una cara feliz o triste, y su elección se escribe en un atributo personalizado de cadena llamado feedback_sentiment.
Pega lo siguiente en un único bloque de Código personalizado (HTML):
<div id="sentiment-picker" class="sentiment-picker">
<button type="button" data-sentiment="positive" aria-label="Happy">🙂</button>
<button type="button" data-sentiment="negative" aria-label="Unhappy">🙁</button>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const picker = document.getElementById("sentiment-picker");
picker.querySelectorAll("button").forEach((button) => {
button.addEventListener("click", () => {
picker.dataset.sentiment = button.dataset.sentiment;
picker.querySelectorAll("button").forEach((b) => b.classList.remove("selected"));
button.classList.add("selected");
});
});
window.brazeHelpers.forms.registerFormInput({
selector: "#sentiment-picker",
isRequired: true,
getValue: (element) => element.dataset.sentiment ?? null,
onSubmit: (value) => {
window.brazeBridge.getUser().setCustomUserAttribute("feedback_sentiment", value);
},
});
});
</script>
<style>
.sentiment-picker {
display: flex;
gap: 16px;
justify-content: center;
font-size: 40px;
}
.sentiment-picker button {
background: none;
border: 2px solid transparent;
border-radius: 12px;
cursor: pointer;
line-height: 1;
padding: 8px;
}
.sentiment-picker button.selected {
border-color: #1f2933;
}
/* Braze adds this class to the registered element when validation fails. */
.sentiment-picker.bz-validation-error {
outline: 3px solid #f94144;
outline-offset: 4px;
border-radius: 12px;
}
</style>
Cómo funciona: Al seleccionar un botón, se almacena su valor data-sentiment en el elemento contenedor. getValue lee ese valor del elemento contenedor que Braze le pasa. Dado que isRequired es true, el formulario no se envía hasta que el visitante elige una cara, y el contenedor se marca con la clase bz-validation-error mientras está vacío. Al enviar, el valor elegido ("positive" o "negative") se escribe en feedback_sentiment.
Objetivo: El visitante raspa una tarjeta para revelar uno de tres descuentos (10% Off, 20% Off o 25% Off), y el descuento se escribe en un atributo personalizado de cadena llamado scratch_off_reward.
Este ejemplo dibuja una tarjeta de raspa y gana en un Canvas HTML. Se elige una recompensa al azar cuando se carga la página y se oculta bajo una capa opaca que el visitante rasca. Puedes sustituir el enfoque de Canvas por cualquier widget de rasca que prefieras; solo la llamada registerFormInput lo conecta a Braze. Pega lo siguiente en un único bloque de Código personalizado (HTML):
<div id="scratch-card" class="scratch-card" data-reward="">
<span class="scratch-card__reward"></span>
<canvas class="scratch-card__surface" width="300" height="150"></canvas>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const rewards = ["10% Off", "20% Off", "25% Off"];
const card = document.getElementById("scratch-card");
const label = card.querySelector(".scratch-card__reward");
const canvas = card.querySelector(".scratch-card__surface");
const ctx = canvas.getContext("2d");
// Randomly assign which reward this visitor will reveal.
const reward = rewards[Math.floor(Math.random() * rewards.length)];
label.textContent = reward;
// Paint the opaque scratch layer over the reward.
ctx.fillStyle = "#b3b3b3";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "destination-out";
let isScratching = false;
function scratchAt(event) {
const rect = canvas.getBoundingClientRect();
ctx.beginPath();
ctx.arc(event.clientX - rect.left, event.clientY - rect.top, 18, 0, Math.PI * 2);
ctx.fill();
}
canvas.addEventListener("pointerdown", () => { isScratching = true; });
canvas.addEventListener("pointermove", (event) => {
if (isScratching) scratchAt(event);
});
canvas.addEventListener("pointerup", () => {
isScratching = false;
// The visitor has scratched the card, so record the revealed reward.
card.dataset.reward = reward;
});
window.brazeHelpers.forms.registerFormInput({
selector: "#scratch-card",
isRequired: true,
getValue: (element) => element.dataset.reward || null,
onValidate: (value) => rewards.includes(value),
onSubmit: (value) => {
window.brazeBridge.getUser().setCustomUserAttribute("scratch_off_reward", value);
},
});
});
</script>
<style>
.scratch-card {
position: relative;
width: 300px;
height: 150px;
margin: 0 auto;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
/* The reward sits underneath and is revealed as the canvas is scratched away. */
.scratch-card__reward {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
font-weight: 700;
color: #1f2933;
}
.scratch-card__surface {
position: absolute;
inset: 0;
border-radius: 12px;
cursor: pointer;
touch-action: none;
}
/* Braze adds this class to the registered element when validation fails. */
.scratch-card.bz-validation-error {
outline: 3px solid #f94144;
outline-offset: 4px;
border-radius: 12px;
}
</style>
Cómo funciona: Cuando se carga la página, el script elige aleatoriamente una de las tres recompensas y pinta una capa opaca sobre ella en el Canvas. A medida que el visitante arrastra el cursor por el Canvas, el modo compuesto "destination-out" borra esa capa y revela la recompensa debajo. Al soltar (pointerup), la recompensa revelada se escribe en el atributo data-reward de la tarjeta. getValue la lee desde ahí, onValidate confirma que es una de las tres recompensas definidas e isRequired impide el envío hasta que la tarjeta haya sido rascada. Al enviar, el descuento revelado (por ejemplo, "20% Off") se escribe en scratch_off_reward.
Objetivo: Capturar la Campaign que dirigió al visitante a la página de destino y registrarla como una propiedad de evento personalizado para informes y atribución posteriores.
Este ejemplo muestra cómo atribuir el envío de un formulario de página de destino a una Campaign específica. Al agregar una variable Liquid como {{campaign.${api_id}}} a la URL de tu página de destino en mensajes de correo electrónico, SMS o WhatsApp, puedes pasar el identificador de la Campaign a la página de destino. El bloque de formulario personalizado lee este parámetro de la URL y lo registra como un evento personalizado con el ID de API de la Campaign como propiedad del evento, lo que facilita el seguimiento de qué Campaigns impulsan los envíos de formularios.
Pega lo siguiente en un único bloque de Código personalizado (HTML):
<input type="hidden" id="campaign-attribution" value="" />
<script>
document.addEventListener("DOMContentLoaded", () => {
const hiddenInput = document.getElementById("campaign-attribution");
const campaignApiId = new URLSearchParams(window.location.search).get("campaign_api_id");
if (campaignApiId) {
hiddenInput.value = campaignApiId;
}
window.brazeHelpers.forms.registerFormInput({
selector: "#campaign-attribution",
isRequired: false,
getValue: (element) => element.value || null,
onSubmit: async (value) => {
if (!value) {
return;
}
await window.brazeBridge.logCustomEvent("landing_page_form_submitted", {
campaign_api_id: value,
});
},
});
});
</script>
Cómo funciona: Cuando creas un mensaje de correo electrónico, SMS o WhatsApp que enlaza a tu página de destino, agrega el identificador de la Campaign a la URL usando plantillas Liquid: https://your-landing-page.com?campaign_api_id={{campaign.${api_id}}}. Cuando un visitante llega a la página de destino desde ese mensaje, el script lee el parámetro campaign_api_id de la URL y lo almacena en un campo de entrada oculto. Al enviar el formulario, si hay un ID de Campaign presente, la devolución de llamada onSubmit registra un evento personalizado llamado landing_page_form_submitted con el ID de API de la Campaign como propiedad del evento. Este evento aparece en Currents y puede usarse para informes, segmentación y análisis de atribución.

Puedes extender este patrón para capturar parámetros de URL adicionales como la variación del mensaje, el paso en Canvas o cualquier otra variable Liquid que desees pasar a la página de destino con fines de atribución.
Validación y campos obligatorios
Una entrada debe superar todas las siguientes capas que le apliquen antes de que el formulario pueda enviarse:
isRequiredcondiciona el envío a la presencia de un valor no vacío. DevuelvenulldesdegetValuecuando la entrada aún no tiene valor para que Braze pueda detectar que está vacía. Las cadenas vacías (incluidas las que solo contienen espacios en blanco) y los arrays vacíos también se tratan como vacíos, mientras que0yfalsecuentan como valores presentes.isRequiredpuede ser un booleano o una promesa que se resuelve en uno, y se vuelve a evaluar cada vez que se valida la entrada.- Validación nativa de restricciones. Si el elemento coincidente admite la API estándar de HTML
checkValidity(), por ejemplo un<input>nativo conrequired,pattern,minomax, Braze la ejecuta y bloquea el envío cuando falla. Para elementos completamente personalizados y no nativos (undiv, uncanvas, etc.), esta comprobación siempre pasa, por lo que nunca interfiere con tu propia lógica. onValidatecondiciona el envío a tus propias reglas. Recibe el valor actual y el elemento coincidente, y debe devolverboolean \| Promise<boolean>—el mismo tipo de retorno queisRequired— dondetruesignifica que el valor es válido yfalsesignifica que no lo es. Úsalo para comprobaciones de valores permitidos, comprobaciones de formato, rangos o cualquier lógica que puedas expresar en JavaScript.
Estilo de error
Cada vez que una entrada falla la validación, Braze añade la clase CSS bz-validation-error al elemento coincidente con tu selector o element, y elimina la clase cuando la entrada vuelve a ser válida. Puedes dar el estilo que quieras al estado no válido, por ejemplo un contorno o borde que llame la atención sobre la entrada incorrecta, añadiendo una regla que apunte a tu elemento combinada con la clase bz-validation-error:
#my-custom-input.bz-validation-error {
outline: 3px solid #f94144;
outline-offset: 4px;
}
Dar estilo al estado de error es opcional pero recomendable, para que los visitantes puedan ver qué entrada personalizada está bloqueando el envío. Cada ejemplo en esta página incluye una regla bz-validation-error.
Prácticas recomendadas
- Usa un selector estable y único. Un
ides la opción más segura. Evita selectores que puedan coincidir con más de un elemento. - Devuelve
null, no una cadena vacía niundefined, cuando no haya valor, para que las comprobaciones de campos obligatorios se comporten de forma predecible. Las cadenas vacías y los arrays vacíos también se tratan como vacíos, peronulles la señal más clara de “sin valor”. - Mantén
getValueeconómico y síncrono. Braze puede llamarlo más de una vez, así que debería leer y devolver el valor actual en lugar de realizar operaciones costosas. - Aplica estilo al estado
bz-validation-errorpara que los visitantes puedan ver qué entrada personalizada está bloqueando el envío. - Define los nombres de tus atributos personalizados con antelación y mantenlos consistentes para poder segmentar los datos de forma fiable más adelante.
- Prueba el envío completo. Confirma que el atributo aparece en el perfil de usuario después de enviarlo, y que las reglas de campos obligatorios y de validación bloquean el envío según lo esperado.
Solución de problemas
El formulario se envía aunque no se seleccionó nada
Asegúrate de que isRequired esté establecido en true. Braze trata null, undefined, cadenas vacías (incluidas las cadenas que solo contienen espacios en blanco) y arrays vacíos como ausencia de valor. Si getValue devuelve algo diferente (por ejemplo, un valor predeterminado no vacío o un marcador de posición) cuando no se ha seleccionado nada, la verificación de campo obligatorio no lo detectará.
El valor no aparece en el perfil
Confirma que onSubmit llama a window.brazeBridge.getUser().setCustomUserAttribute con el nombre de atributo correcto, y que el bloque Custom Code está en la misma página de destino que el formulario.
El registro parece no hacer nada
Comprueba que el selector coincida con un elemento que exista en el DOM cuando se ejecuta registerFormInput, y que el script se ejecute después de que ese elemento se haya renderizado. A continuación, abre la consola para desarrolladores de tu navegador: registerFormInput valida su configuración y, cuando algo no es correcto (por ejemplo, un getValue ausente, un selector que no es un selector CSS válido o una propiedad del tipo incorrecto), ignora el registro y muestra una advertencia con el prefijo [brazeHelpers.forms.registerFormInput] que describe qué era inválido.