Integra el componente Legalesign Signer en tu sitio web
El componente Signer requiere activación. Contacta con soporte para habilitarlo para tu equipo.
El Legalesign Signer es un componente web agnóstico de plataforma que proporciona una experiencia completa y fluida de firma de documentos desde tu propia aplicación. Funciona con HTML vanilla, React, Vue, Angular o cualquier framework web.
Inserta este componente donde tus usuarios necesiten firmar documentos: dentro de un CRM, portal de clientes o cualquier aplicación interna que renderice HTML.
Instalación
Carga directamente desde el CDN (no requiere instalación):
<script type="module" src="https://cdn.legalesign.io/signer/latest/ls-signer.esm.js"></script>
Instalación en React
Instala mediante un gestor de paquetes:
npm install legalesign-signer
# or
pnpm add legalesign-signer
Integración básica
HTML y JavaScript
<ls-signer
recipient-id="abc123"
private-key="key"
session-id="session"
></ls-signer>
<script>
const signer = document.querySelector('ls-signer');
signer.addEventListener('signingSuccess', (e) => console.log('Signed:', e.detail.eventType, e.detail.documentId));
signer.addEventListener('signingFail', (e) => console.error('Failed:', e.detail.failureReason, e.detail.error));
signer.addEventListener('fieldChange', (e) => console.log('Field event:', e.detail.eventType));
</script>
React
import { useEffect, useState } from 'react';
import { LsSigner } from 'legalesign-signer/react';
import 'legalesign-signer/react.css';
function SigningPage() {
const [signerData, setSignerData] = useState(null);
useEffect(() => {
fetch('/your-backend/to-get-token')
.then(res => res.json())
.then(setSignerData);
}, []);
if (!signerData) return <p>Loading...</p>;
return (
<LsSigner
recipientId={signerData.recipientId}
privateKey={signerData.token}
sessionId={signerData.sessionId}
onSuccess={({ eventType, documentId }) => console.log(eventType, documentId)}
onFail={({ eventType, failureReason, error }) => console.error(eventType, failureReason, error)}
onChange={({ eventType, uuid, saved, field, document }) => console.log('Event:', eventType)}
/>
);
}
React requiere react y react-dom (v18 o v19) como dependencias peer.
Vue
<template>
<ls-signer
ref="signer"
:recipient-id="recipientId"
:private-key="privateKey"
:session-id="sessionId"
/>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const signer = ref(null);
function handleSuccess(e) {
console.log('Signed:', e.detail);
}
function handleFail(e) {
console.error('Failed:', e.detail);
}
onMounted(() => {
signer.value.addEventListener('signingSuccess', handleSuccess);
signer.value.addEventListener('signingFail', handleFail);
});
onBeforeUnmount(() => {
signer.value?.removeEventListener('signingSuccess', handleSuccess);
signer.value?.removeEventListener('signingFail', handleFail);
});
</script>
Si usas Vue con Vite, añade isCustomElement: (tag) => tag.startsWith('ls-') a la configuración del plugin Vue para suprimir las advertencias de elementos desconocidos.
Vue 3 convierte a minúsculas los nombres de eventos en elementos personalizados, por lo que @signingSuccess no funcionará. Usa addEventListener en la referencia del elemento como se muestra arriba.
Envuelve <ls-signer> en <ClientOnly> para evitar errores SSR. Añade la configuración de elementos personalizados en nuxt.config.ts:
export default defineNuxtConfig({
vue: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('ls-'),
},
},
});
Autenticación
El componente signer requiere un token de corta duración desde tu backend. Hay dos opciones:
- GraphQL
generateComponentToken— Llama concomponent: LS_SIGNERy ya sea unrecipientIdosessionIden el scope del signer (proporciona uno, no ambos). - REST API — Llama a
GET /signer/{signerId}/component-token/con tu clave API.
Ambos retornan el token y sessionId que el componente necesita. Consulta Widget Authorization para el flujo completo del token del lado servidor.
Opción 1: GraphQL (Node.js)
Usando recipientId (más común — usar cuando conoces al destinatario pero aún no tienes sesión):
const response = await fetch('https://graphql.uk.legalesign.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LEGALESIGN_API_KEY}`,
},
body: JSON.stringify({
query: `mutation MintSignerToken($input: GenerateComponentTokenInput!) {
generateComponentToken(input: $input) {
token
tokenType
sessionId
expiresIn
expiresAt
}
}`,
variables: {
input: {
component: 'LS_SIGNER',
signer: { recipientId: '<recipient-id>' }
}
}
})
});
const { data } = await response.json();
const { token, sessionId } = data.generateComponentToken;
Alternativamente, si ya tienes un sessionId de una llamada previa:
variables: {
input: {
component: 'LS_SIGNER',
signer: { sessionId: '<session-id>' }
}
}
Opción 2: REST API
const response = await fetch(
`https://eu-api.legalesign.com/api/v1/signer/${signerId}/component-token/`,
{
headers: {
Authorization: `Bearer ${process.env.LEGALESIGN_API_KEY}`,
},
}
);
const { token, sessionId } = await response.json();
Pasa token como private-key y sessionId como session-id al componente.
Atributos requeridos
| Atributo | Tipo | Descripción |
|---|---|---|
recipient-id | string | Identificador del destinatario (base64, prefijo rec o UUID) |
private-key | string | Token retornado por generateComponentToken |
session-id | string | ID de sesión retornado por generateComponentToken |
Atributos opcionales
| Atributo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
colour | string | blue | Nombre del color del tema (ver Theming) |
style | string | `` | Alteraciones de estilo (ver Customer CSS) |
branding | boolean | true | Remover la marca Legalesign |
language | string | — | Forzar un idioma específico y ocultar el selector de idioma (ver Internationalisation) |
Eventos
| Evento componente web | Propiedad React | eventType | Cuándo |
|---|---|---|---|
signingSuccess | onSuccess | "success" | Documento firmado con éxito |
signingFail | onFail | "failure" | Firma fallida, rechazada, expirada, cancelada, eliminada o removida |
fieldChange | onChange | "ready" | Documento e imágenes cargadas completamente |
fieldChange | onChange | "save" | Se guardó un valor de campo |
fieldChange | onChange | "select" | Se seleccionó o enfocó un campo |
signingSuccess / onSuccess
{
eventType: 'success';
documentId: string;
recipientId: string;
}
signingFail / onFail
{
eventType: 'failure';
failureReason: FailureReason;
error: string;
documentId?: string;
recipientId: string;
}
failureReason | Causa |
|---|---|
documentExpired | Documento expirado o fecha de expiración pasada |
sessionExpired | API devolvió 401 o 403 |
cancelled | Estado del documento es "cancelled" |
deleted | Documento ha sido eliminado |
rejected | Destinatario rechazó el documento |
removed | API devolvió 404 |
signingError | Cualquier otro error durante la carga o firma |
fieldChange / onChange
Los tres valores eventType comparten el mismo evento. Usa eventType para distinguir:
// eventType: 'ready' — document fully loaded
{ eventType: 'ready'; document: { documentId, name, state, recipientId, pageCount, expires?, isApprover, isWitness, canReassign, doOfferReject } }
// eventType: 'save' — field value saved
{ eventType: 'save'; uuid: string; saved: boolean; field: Record<string, any> }
// eventType: 'select' — field selected or focused
{ eventType: 'select'; uuid: string; field: Record<string, any> }
Propiedades React
El componente React usa props en camelCase y callbacks en lugar de eventos DOM:
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
recipientId | string | ✅ | Identificador del destinatario |
privateKey | string | ✅ | Clave privada del mutation generateComponentToken |
sessionId | string | ✅ | Identificador de sesión |
colour | LsSignerColour | ❌ | Color del tema |
branding | boolean | ❌ | Mostrar marca Legalesign. Por defecto true. |
language | string | ❌ | Forzar un idioma específico (ver Internationalisation) |
onSuccess | (data: { eventType: 'success'; documentId: string; recipientId: string }) => void | ❌ | Llamado tras firma exitosa |
onFail | (data: { eventType: 'failure'; failureReason: FailureReason; error: string; documentId?: string; recipientId: string }) => void | ❌ | Llamado tras fallo en la firma |
onChange | (data: { eventType: 'ready' | 'save' | 'select'; document?: DocumentSummary; uuid?: string; saved?: boolean; field?: Record<string, any> }) => void | ❌ | Llamado en eventos de campos y carga del documento |
Manejo global (depuración y prototipado)
Como cada evento incluye eventType, puedes enlazar una única función a todos los eventos:
React:
const handleSignerEvent = (e) => {
switch (e.eventType) {
case 'success': console.log('Signed:', e.documentId); break;
case 'failure': console.log('Failed:', e.failureReason, e.error); break;
case 'ready': console.log('Ready:', e.document?.name); break;
case 'save': console.log('Saved field:', e.uuid, e.saved); break;
case 'select': console.log('Selected field:', e.uuid); break;
}
};
<LsSigner
onSuccess={handleSignerEvent}
onFail={handleSignerEvent}
onChange={handleSignerEvent}
/>
Componente web:
const handleSignerEvent = (e) => {
switch (e.detail.eventType) {
case 'success': console.log('Signed:', e.detail.documentId); break;
case 'failure': console.log('Failed:', e.detail.failureReason, e.detail.error); break;
case 'ready': console.log('Ready:', e.detail.document?.name); break;
case 'save': console.log('Saved field:', e.detail.uuid, e.detail.saved); break;
case 'select': console.log('Selected field:', e.detail.uuid); break;
}
};
signer.addEventListener('signingSuccess', handleSignerEvent);
signer.addEventListener('signingFail', handleSignerEvent);
signer.addEventListener('fieldChange', handleSignerEvent);
Tematización
Define un tema de color con la prop colour. Todos los tonos se derivan automáticamente.
Colores disponibles
pink · blue · purple · indigo · teal · green · lightblue · burnt · aubergine · red · yellow · cyan · lime · trueGreen
Omitir la prop para el azul por defecto.
<ls-signer colour="pink" ...></ls-signer>
<LsSigner colour="pink" ... />
La prop colour establece un atributo data-ls-theme en la raíz del componente. Las propiedades CSS personalizadas definen una escala de tonos de 10 a 100 para cada color:
| Tono | Uso |
|---|---|
| 10 | Fondos claros, rellenos sutiles |
| 20 | Bordes sutiles |
| 30 | Anillos de enfoque |
| 60 | Color primario (botones, enlaces, estados activos) |
| 70 | Estado hover |
| 80 | Acentos oscuros/fuertes |
CSS personalizado
El componente expone propiedades CSS personalizadas que pueden ser sobrescritas para personalizar la apariencia más allá del tema de color. Este conjunto de propiedades es limitado actualmente, contáctanos para más detalles.
<ls-signer
style="--ls-font-family: 'Inter', sans-serif; --ls-color-primary-60: #e91e63;"
recipient-id="abc123"
private-key="key"
session-id="session"
></ls-signer>
Propiedades disponibles
| Propiedad | Valor predeterminado | Descripción |
|---|---|---|
--ls-font-family | 'IBM Plex Sans', sans-serif | Familia de fuente principal |
--ls-color-primary-10 a --ls-color-primary-100 | — | Escala completa del color primario (sobrescribe el tema) |
--ls-color-error | #f64a44 | Color para estado de error |
--ls-color-error-light | #fff0f0 | Fondo para error |
--ls-color-success | #46dbaa | Color para estado de éxito |
--ls-color-success-light | #effff9 | Fondo para éxito |
--ls-color-warning | #fad232 | Color para estado de advertencia |
--ls-color-warning-light | #fffcef | Fondo para advertencia |
--ls-color-border | #d8d9dc | Color de borde por defecto |
--ls-color-border-subtle | #e0e2e5 | Color de borde sutil |
--ls-color-bg-subtle | #f7f8fa | Color de fondo sutil |
Nota: Para inyección completa de CSS personalizado (dirigido a elementos internos directamente, sobrescribir tamaños de fuente, radio de bordes, espaciados, etc.), esta es una función planeada a futuro. Por favor contáctanos si estás interesado.
Internacionalización
El componente soporta 17 idiomas con traducciones incluidas en el build. El idioma se detecta automáticamente desde el navegador. Un selector de idioma integrado en la UI de firma permite al destinatario cambiar el idioma en cualquier momento.
Establece el atributo language para forzar un idioma específico. Cuando se proporciona, el selector integrado se oculta y se ignora la detección del navegador.
<ls-signer language="fr" recipient-id="abc123" private-key="key" session-id="session"></ls-signer>
<LsSigner language="fr" recipientId="abc123" privateKey="key" sessionId="session" />
Orden de prioridad: prop language → idioma del navegador → inglés por defecto.
| Código | Idioma | Código | Idioma |
|---|---|---|---|
en | Inglés | nl | Neerlandés |
fr | Francés | fi | Finés |
bg | Búlgaro | it | Italiano |
es | Español | he | Hebreo |
de | Alemán | sv | Sueco |
gs | Gaélico escocés | cy | Galés |
ar | Árabe | is | Islandés |
el | Griego | iw | Hebreo (legado) |
pt | Portugués | ||
ro | Rumano |
Versionado CDN
La URL del CDN soporta tanto latest como números de versión fijos:
<!-- Always get the most recent version (recommended) -->
<script type="module" src="https://cdn.legalesign.io/signer/latest/ls-signer.esm.js"></script>
<!-- Pin to a specific version -->
<script type="module" src="https://cdn.legalesign.io/signer/v1.1.0/ls-signer.esm.js"></script>
Recomendamos usar latest — esto asegura que tu integración reciba automáticamente correcciones de errores, parches de seguridad y nuevas funcionalidades. Usa una versión fija si necesitas bloquear una versión conocida durante una congelación de QA o despliegue controlado. Consulta el historial completo de versiones en npm para versiones disponibles.
Ejemplo completo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document Signing</title>
<script type="module" src="https://cdn.legalesign.io/signer/latest/ls-signer.esm.js"></script>
</head>
<body>
<ls-signer
recipient-id="abc123"
private-key="key"
session-id="session"
colour="teal"
></ls-signer>
<script>
const signer = document.querySelector('ls-signer');
signer.addEventListener('signingSuccess', (event) => {
console.log('Document signed:', event.detail.documentId);
window.location.href = '/thank-you';
});
signer.addEventListener('signingFail', (event) => {
console.error('Signing failed:', event.detail.failureReason, event.detail.error);
});
signer.addEventListener('fieldChange', (event) => {
if (event.detail.eventType === 'ready') {
console.log('Ready to sign:', event.detail.document.name);
}
});
</script>
</body>
</html>
Soporte de navegadores
- Chrome/Edge (últimas versiones)
- Firefox (últimas versiones)
- Safari (últimas versiones)
- Navegadores móviles (iOS Safari, Chrome Mobile)