Skip to content

<co-form> (Web Component)

This content is not available in your language yet.

<co-form> es el Web Component oficial. Mismo motor que el composable Vue, pero accesible desde cualquier framework o desde HTML vanilla.

<!-- Tokens y Web Components Cobalt -->
<link rel="stylesheet" href="https://unpkg.com/@prolibu-suite/cobalt-tokens/build/css/tokens.css">
<script type="module">
import { defineCustomElements as defineCoreElements } from '@prolibu-suite/cobalt-core/loader';
import { defineCustomElements as defineFormElements } from '@prolibu-suite/cobalt-form/loader';
defineCoreElements(window);
defineFormElements(window);
</script>
<!-- Form en HTML -->
<co-form
locale="es"
layout="grid"
schema='{
"email": { "type": "String", "required": true, "format": "email", "label": "Email" },
"password": { "type": "String", "required": true, "minLength": 8, "label": "Contraseña" }
}'
></co-form>
AtributoTipoDefaultDescripción
schemaJSON stringSchema del form. Requerido.
initial-valuesJSON stringValores iniciales (mergeados con defaults del schema)
locale'es' | 'en' | 'pt-BR''en'Idioma de mensajes de error
layout'grid' | 'stack''grid'Layout de campos
exclude-fieldsstring CSVLista separada por comas de campos a ocultar
disabledbooleanfalseDeshabilita todos los campos
read-onlybooleanfalseModo lectura
PropertyTipoDescripción
schemaInstanceFormSchemaLikeSchema pre-construido (alternativa a schema)
refResolverRefResolverFunción para resolver opciones de campos ref
const form = document.querySelector('co-form');
form.refResolver = async ({ model, query, page, pageSize = 20, ids }) => {
const params = new URLSearchParams({ q: query, page, pageSize });
if (ids) params.set('ids', ids.join(','));
const res = await fetch(`/api/${model.toLowerCase()}?${params}`);
const { data, total } = await res.json();
return {
options: data.map((item) => ({
value: item._id,
label: item.name,
})),
total,
hasMore: page * pageSize < total,
};
};

Todos los métodos son async:

const form = document.querySelector('co-form');
await form.setValue('email', 'foo@bar.com');
const email = await form.getValue('email');
await form.touch('password');
form.addEventListener('coSubmit', (e) => {
console.log('values:', e.detail.values);
});
form.addEventListener('coChange', (e) => {
console.log(`${e.detail.name} →`, e.detail.value);
});
form.addEventListener('coValidate', (e) => {
console.log('isValid:', e.detail.isValid);
console.log('errors:', e.detail.errors);
});
Eventodetail
coSubmit{ values: Record<string, any> }
coChange{ name: string; value: any }
coValidate{ isValid: boolean; errors: Record<string, string[]> }
SlotContenido
headerAparece arriba de los campos
footerAparece abajo (por defecto: botón submit)
field:<name>Override de un campo específico
<co-form schema="..." locale="es">
<h2 slot="header">Nuevo usuario</h2>
<div slot="footer">
<co-button label="Cancelar" variant="ghost"></co-button>
<co-button label="Crear" variant="primary"></co-button>
</div>
</co-form>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/@prolibu-suite/cobalt-tokens/build/css/tokens.css">
</head>
<body>
<co-form id="signup" locale="es" layout="grid"></co-form>
<script type="module">
import { defineCustomElements as defineCoreElements } from '@prolibu-suite/cobalt-core/loader';
import { defineCustomElements as defineFormElements } from '@prolibu-suite/cobalt-form/loader';
defineCoreElements(window);
defineFormElements(window);
const form = document.getElementById('signup');
form.schemaInstance = {
firstName: { type: 'String', required: true, label: 'Nombre' },
email: { type: 'String', required: true, format: 'email', label: 'Email' },
password: { type: 'String', required: true, minLength: 8, label: 'Contraseña' },
newsletter: { type: 'Boolean', default: true, label: 'Newsletter' },
};
form.addEventListener('coSubmit', async (e) => {
const { values } = e.detail;
const res = await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values),
});
if (res.ok) alert('Cuenta creada!');
});
</script>
</body>
</html>
CoFormReact.jsx
import { useEffect, useRef } from 'react';
export function CoFormReact({ schema, initialValues, locale = 'es', onSubmit }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
el.schemaInstance = schema;
if (initialValues) el.initialValues = JSON.stringify(initialValues);
const handler = (e) => onSubmit?.(e.detail.values);
el.addEventListener('coSubmit', handler);
return () => el.removeEventListener('coSubmit', handler);
}, [schema, initialValues, onSubmit]);
return <co-form ref={ref} locale={locale} layout="grid" />;
}

Y en TypeScript necesitas declarar el tag:

types.d.ts
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'co-form': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
schema?: string;
locale?: string;
layout?: string;
},
HTMLElement
>;
}
}
}
AspectouseForm + CoFormRenderer<co-form>
Bundle size adicional+ form-vue (~5KB)+ cobalt-form (~8KB)
Reactivity nativaSí (refs Vue)Vía eventos DOM
Slots / overridesSí, field:<name>Sí, field:<name>
State accesibleRefs computadosform.getValue() (async)
SSRLimitadoNo (custom elements son cliente-only)
Mejor para…Apps VueHTML, otros frameworks, CDN