Recetas de integración
Siete casos habituales de component-limbo, con código que puedes copiar.
🔑 Token desde tu backend (producción)
Problema: dar token al navegador sin exponer credenciales. Tu backend expone un endpoint propio (aquí
/api/limbo-token) que llama a POST https://limbo.lefebvre.es/api/v1/auth/token con
{ "public_key": "pk_de_tu_api_key" } y devuelve al navegador { token, expires_in }.
Cachea el token hasta poco antes de expires_in: /api/v1/auth/token tiene límite de
peticiones y responde 429 con la cabecera Retry-After si te pasas.
let cached = null; // { token, expiresAt }
app.get('/api/limbo-token', async (req, res) => {
const now = Date.now();
if (!cached || cached.expiresAt - 30000 <= now) {
const response = await fetch('https://limbo.lefebvre.es/api/v1/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ public_key: process.env.LIMBO_PUBLIC_KEY })
});
if (!response.ok) {
const retryAfter = response.headers.get('Retry-After');
if (retryAfter) res.set('Retry-After', retryAfter);
return res.status(response.status).json({ error: 'limbo_token_failed' });
}
const { data } = await response.json(); // v1: { success, data: { token, expires_in, … } }
cached = { token: data.token, expiresAt: now + data.expires_in * 1000 };
}
res.json({
token: cached.token,
expires_in: Math.floor((cached.expiresAt - now) / 1000)
});
});
Limbo.configure({
publicKey: 'pk_tu_clave_publica',
prod: true,
authMode: 'jwt',
tokenProvider: async () => {
const res = await fetch('/api/limbo-token');
return await res.json(); // { token, expires_in }
}
});
Más sobre los tokens de la API en la documentación de autenticación y sobre los modos del componente en la configuración.
🪟 Elegir un fichero en un modal
Problema: dejar al usuario elegir o subir un fichero y recibir el resultado en un callback.
Limbo.openGallery(options) abre la galería para elegir uno existente y
Limbo.openUploader(options) abre el formulario de subida. Ambos devuelven la instancia.
// Elegir una imagen existente
Limbo.openGallery({
accept: 'image/*',
title: 'Seleccionar imagen',
onSelect: (data) => {
document.getElementById('preview').src = data.master.urlSigned;
}
});
// Subir un documento nuevo
Limbo.openUploader({
accept: '.pdf',
redirectToGallery: false,
onUpload: (data) => {
document.getElementById('pdf-link').href = data.master.urlSigned;
}
});
openGallery admite también size y autoClose. Los datos del callback
son los del evento limbo:select o limbo:upload:
eventos de instancia.
🖼️ Galería embebida en la página
Problema: mostrar la galería de forma permanente dentro de un contenedor.
Limbo.createInlineGallery(container, options) solo ofrece elegir;
Limbo.createFullSelector(container, options) añade subida y recortador.
// Solo galería
Limbo.createInlineGallery('#gallery-container', {
accept: 'image/*',
onSelect: (data) => console.log('Seleccionada:', data.assetId),
onDelete: (assetId) => console.log('Eliminada:', assetId)
});
// Galería + subida + recortador
Limbo.createFullSelector('#limbo-full', {
accept: 'image/*,.pdf',
onSelect: (data) => console.log('Seleccionado:', data.assetId),
onUpload: (data) => console.log('Subido:', data.assetId),
onCropsSaved: (data) => console.log('Recortes:', data.crops)
});
Todas las opciones de la instancia están en la
referencia de Limbo.create.
🧲 Limitar tipos de fichero
Problema: aceptar solo ciertos ficheros. validation.accept usa la sintaxis del atributo
accept de HTML; los métodos prefab (openGallery, openUploader,
createInlineGallery, createFullSelector...) lo aceptan directamente como opción
accept. Sin él, se permite todo.
// Solo imágenes
Limbo.create({ container: '#a', validation: { accept: 'image/*' } });
// Solo PDF
Limbo.create({ container: '#b', validation: { accept: '.pdf' } });
// Imágenes o PDF
Limbo.create({ container: '#c', validation: { accept: 'image/*,.pdf' } });
Valores habituales: image/*, video/*, audio/*, document/*
o extensiones como .pdf,.docx. Si accept excluye las imágenes, el componente oculta
las pestañas de generación con IA, de stock y de otros portales, y desactiva el recortador. En un input de
AutoInputs se indica con data-accept
(atributos).
✂️ Recortes obligatorios
Problema: exigir dimensiones fijas (por ejemplo, un banner en escritorio y móvil) al elegir una imagen. Se
definen en cropper.mandatoryCrops. Cada recorte admite label, width,
height, required y format; si no fijas width y
height a la vez, el recorte es libre y puedes acotarlo con minWidth,
minHeight, maxWidth y maxHeight.
Limbo.create({
container: '#banner-selector',
mode: 'modal',
modeUI: 'full',
cropper: {
mandatoryCrops: [
{ label: 'Desktop', width: 1920, height: 400, required: true },
{ label: 'Mobile', width: 640, height: 300, required: true },
{ label: 'Libre', maxWidth: 500, required: false }
],
allowCustomCrops: false
},
callbacks: {
onCropsSaved: (data) => {
data.crops.forEach((crop) => console.log(crop.name, crop.url, crop.width, crop.height));
}
}
});
Para recortar una imagen que ya tienes, pasa su URL a Limbo.openCropper(imageUrl, options):
Limbo.openCropper('https://example.com/foto.jpg', {
mandatoryCrops: [
{ label: 'Thumbnail', width: 300, height: 300, required: true }
],
onComplete: (data) => console.log('Recortes:', data.crops),
onCancelled: () => console.log('Cancelado'),
onError: (error) => console.error(error)
});
📝 Rellenar un input de formulario (AutoInputs)
Problema: que un formulario existente guarde la URL o el ID del fichero sin escribir el selector a mano.
Marca el input con data-limbo-input-file; el componente oculta el input, añade un botón y escribe
el resultado en él.
<input type="hidden" name="avatar" data-limbo-input-file
data-accept="image/*" data-returntype="url">
// Tras Limbo.configure({ ... })
Limbo.configureAutoInputs({
buttonText: 'Elegir imagen'
});
const input = document.querySelector('input[name="avatar"]');
input.addEventListener('limbo:resultReady', (e) => {
console.log('Valor del input:', e.detail.value);
});
Todos los atributos data-* están en la
configuración
y el detalle del evento en el
catálogo de eventos.
📁 Subir a una carpeta y limitar carpetas
Problema: ordenar los ficheros por carpetas del portal. Con folders.defaultFolder las subidas
van a una carpeta; con include o exclude limitas las carpetas visibles, por slug.
allowCreateFolder permite crear carpetas nuevas.
// Galería limitada a dos carpetas
Limbo.create({
container: '#limbo',
mode: 'embed',
modeUI: 'full',
folders: {
showFolderFilter: true,
showFolderInfo: true,
allowMoveFolder: true,
allowCreateFolder: true,
include: ['productos', 'marketing']
// exclude: ['privado']
}
});
// Subida que cae en una carpeta por defecto
Limbo.create({
container: '#uploader',
mode: 'embed',
modeUI: 'upload-only',
folders: {
showFolderSelector: true,
defaultFolder: 'marketing'
}
});