PageKit: el creador de sitios GrapesJS autoalojado, con el código fuente incluido. Obtener acceso anticipado

Tutorial paso a paso

Tutorial de GrapesJS: crea tu primer editor visual

Aprende GrapesJS paso a paso. Construye tu primer editor visual, añade bloques y componentes personalizados, configura estilos y recursos, guarda proyectos, exporta HTML/CSS, instala plugins e integra GrapesJS con React, Vue, Angular o Next.js.

  • Marco de edición de código abierto
  • Autoalojable
  • Extensible con plugins
  • Componentes personalizados
  • Exportación HTML/CSS
  • React / Vue / Angular / vanilla JS

El núcleo de GrapesJS se publica bajo la licencia BSD-3-Clause; el wrapper oficial de React @grapesjs/react es MIT. Ambos permiten uso comercial. Cada ejemplo de esta página se verificó con GrapesJS 0.23.6.

El resultado

Lo que vas a construir

Al final de este tutorial, tendrás un editor visual funcional de arrastrar y soltar que puede crear páginas, editar componentes, gestionar estilos y recursos, guardar proyectos y exportar los HTML y CSS resultantes.

  • Arrastrar bloques sobre un lienzo
  • Seleccionar y editar componentes
  • Rediseñar cualquier cosa de un Style Manager
  • Cambiar entre anchos de escritorio, tablet y teléfono
  • Lee el proyecto de nuevo como JSON
  • Exporta la página como HTML y CSS

Ruta de aprendizaje estimada

Principiante → Intermedio → Producción

El tiempo que tarde depende enteramente de cuánto de la capa de producción necesite tu producto. Los pasos 1–6 son una tarde; los pasos 7–12 son el trabajo.

Abrir la demo oficial de GrapesJS
Requisitos previos

Antes de que empieces

Muy poco. Si sabes escribir una página a mano, puedes seguir esto.

HTML y CSS básicos

Deberías reconocer una etiqueta, una clase y una propiedad CSS. GrapesJS edita HTML y CSS — nada más exótico.

JavaScript básico

Suficiente para leer un objeto literal y una función. Todos los ejemplos aquí son JavaScript puro.

Node.js y npm

Solo si instalas desde npm. La ruta de CDN en el paso 1 solo necesita un editor de texto y un navegador.

No necesitas experiencia previa en GrapesJS, ni un framework. React, Vue, Angular y Next.js se cubren en el paso 11, una vez que el núcleo esté claro.

Principiante

1. Instalar GrapesJS

GrapesJS es una librería que añades a tu propia aplicación, no un servicio al que te inscribas. Hay dos formas de entrar, y la que elijas no afecta a ningún paso posterior.

De npm

Esta es la ruta a seguir si tienes algún tipo de paso de compilación. Instala el editor y su hoja de estilos en tu proyecto; nada se recupera de un host de terceros en tiempo de ejecución.

terminal
npm install grapesjs

De un CDN

No tienes ninguna herramienta de compilación: dos etiquetas en un archivo HTML y tienes un editor. Perfecto para una primera vista o un prototipo. Para cualquier cosa que envíes, pinea una versión exacta en lugar de seguir la última versión.

index.html
<link
  rel="stylesheet"
  href="https://unpkg.com/grapesjs/dist/css/grapes.min.css"
/>
<script src="https://unpkg.com/grapesjs"></script>

<div id="gjs"></div>

<script>
  // The UMD build puts the library on window.grapesjs
  const editor = grapesjs.init({ container: '#gjs' });
</script>

Lo que acabas de instalar

El núcleo editorial

El lienzo, el árbol de componentes, arrastrar y soltar, deshacer/rehacer, los paneles y los gestores para bloques, estilos, recursos, traits, capas y almacenamiento.

Una hoja de estilo

grapesjs/dist/css/grapes.min.css — el propio Chrome del editor. Sin él tienes un editor funcional que parece roto.

Sin bloqueos

El núcleo incluye una paleta de bloques vacía. Los bloques de columna/texto/imagen familiares provienen de un plugin, que es el paso 3.

Sin backend

Sin cuentas, sin base de datos, sin alojamiento. GrapesJS se ejecuta en el navegador y entrega los datos a tu aplicación, que es el paso 7.

Dónde se sitúa GrapesJS en tu aplicación

Es un componente del lado del cliente. Tu aplicación autentica al usuario, decide qué proyecto abrir, monta el editor en un elemento DOM y recibe los datos del proyecto cuando se guardan. Todo lo que esté por encima y por debajo del editor es tuyo.

Principiante

2. Crea tu primer editor GrapesJS

La aplicación GrapesJS más pequeña y útil es un elemento vacío y una llamada. Copia ambos bloques de abajo en una página y tendrás un editor funcional.
index.html
<!-- The editor takes over this element completely.
     Do not render anything inside it yourself. -->
<div id="gjs"></div>
Un elemento. GrapesJS reemplaza su contenido por completo, así que nunca renderizes tu propio UI dentro de él.
editor.js
import grapesjs from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';

const editor = grapesjs.init({
  // Where the editor mounts: a selector or an HTMLElement.
  container: '#gjs',
  height: '100vh',
  width: 'auto',

  // Do not adopt the markup already inside #gjs...
  fromElement: false,
  // ...load this instead. Strings are parsed into components.
  components: `
    <section class="hero">
      <h1>Hello GrapesJS</h1>
      <p>Drag a block from the panel on the right.</p>
    </section>`,
  style: `
    .hero { padding: 64px 32px; font-family: system-ui, sans-serif; }
    .hero h1 { margin: 0 0 12px; font-size: 40px; }`,

  // Storage is ON by default and writes to localStorage.
  // Turn it off until you have decided where projects really live.
  storageManager: false,
});
Dos opciones aquí merecen la pena conocer desde el primer día: fromElement decide si el editor adopta el marcado que ya está dentro del contenedor, y storageManager por defecto usa una tienda respaldada por localStorage que guarda silenciosamente el lienzo en el navegador del lector. Desactivarlo ahora evita una cantidad confusa de estado fantasma más adelante.

Qué significa cada parte

contenedor
El elemento en el que se monta el editor — un selector CSS o un HTMLElement. Dale una altura real, o el editor renderiza cero píxeles en altura.
Instancia de editor
Lo que devuelve init(). Cada API en este tutorial cuelga de él, y llamar a destroy() en él libera el DOM y a los oyentes.
Lienzo
El iframe de tu página está editado por dentro. Como es un iframe real, el CSS de tu página no puede filtrarse en él, y su CSS no puede salir.
Proyecto inicial
Con qué empieza el lienzo — los componentes y opciones de estilo, o lo que sea que cargue la capa de almacenamiento.
Configuración
Un solo objeto. Cada gestor en los pasos posteriores se configura a partir de una clave: blockManager, styleManager, assetManager, storageManager, deviceManager.

Lo que deberías ver

Una interfaz de tres partes: el lienzo en el centro, el conmutador de paneles arriba a la derecha y — una vez que hayas añadido bloques en el paso 3 — una paleta desde la que arrastrar. El editor de arriba de esta sección es exactamente esta configuración con seis bloques y tres anchos de dispositivo añadidos.

Abrir la demo en vivo
Principiante

3. Añadir bloques de arrastrar y soltar

Antes de cualquier código, hay una distinción. Es lo único que los principiantes más suelen fallar, y todo depende de ello a partir de ahora.

Bloquear

Una entrada de paleta. Existe solo en el panel y contiene una receta de qué crear. Tiene una etiqueta, una categoría, un icono y contenido.

Componente

Un nodo en el lienzo. Tiene un tipo, atributos, estilos, hijos, y es lo que se exporta y guarda.

Un bloque es lo que el usuario arrastra al lienzo. Una vez soltado, crea componentes dentro del editor. Un bloque puede crear un subárbol completo de componentes — y el mismo bloque que se cae dos veces crea dos subárboles independientes.

Una paleta inicial

  • Hero
  • Imagen
  • Texto
  • Botón
  • Dos columnas
  • Formulario de contacto
blocks.js
// A Block is a palette entry. Dropping it creates Components.
editor.Blocks.add('hero-section', {
  label: 'Hero',
  category: 'Sections',
  // Shown in the palette. Any HTML string works; an inline SVG keeps it sharp.
  media: '<svg viewBox="0 0 24 24" width="22"><rect x="3" y="5" width="18" height="6" rx="1" fill="currentColor"/><rect x="3" y="13" width="11" height="3" rx="1" fill="currentColor" opacity=".5"/></svg>',
  content: `
    <section class="hero">
      <h1>Headline</h1>
      <p>Supporting copy.</p>
      <a href="#" class="btn">Call to action</a>
    </section>`,
});

// The same block, expressed as a component definition instead of HTML.
// Use this form once you have your own component types (step 10).
editor.Blocks.add('product-card', {
  label: 'Product card',
  category: 'Commerce',
  content: { type: 'product-card' },
});
el contenido acepta una cadena HTML o una definición de componente. Las cadenas son la forma más rápida de empezar; la forma de objeto es a la que cambias una vez que tienes tus propios tipos de componentes en el paso 10.

El núcleo no transporta bloques

Esto sorprende a casi todo el mundo. Un editor de GrapesJS ya de fábrica tiene una paleta vacía — el conocido conjunto de "1 columna / 2 columnas / texto / imagen" que aparece en cada captura de pantalla viene de grapesjs-blocks-basic o de uno de los presets. O escribes tus propios bloques, como antes, o añades un preset.

blocks-preset.js
import grapesjs, { usePlugin } from 'grapesjs';
import blocksBasic from 'grapesjs-blocks-basic';

// The core ships no blocks at all. The familiar
// "1 column / 2 columns / text / image" palette is a plugin.
grapesjs.init({
  container: '#gjs',
  plugins: [usePlugin(blocksBasic, { flexGrid: true })],
});
usePlugin() es la forma actual de registrar un plugin. Los tutoriales antiguos llaman grapesjs.plugins.add(); ese API está obsoleto y registra una advertencia.
Principiante

4. Entiende los componentes GrapesJS

El lienzo no es una cadena de HTML que GrapesJS analiza al guardar. Es un árbol activo de modelos de Componentes, y el HTML se genera a partir de ese árbol. Una vez que eso encaja, el resto del API deja de sorprender.

El vocabulario

Árbol de componentes
Cada nodo en el lienzo es un componente, y cada componente tiene un padre e hijos. La raíz es el envoltorio.
Tipos de componentes
Los tipos integrados incluyen texto, imagen, enlace, vídeo, tabla y un predeterminado genérico. Un tipo decide cómo se comporta, renderiza y exporta un nodo.
Componentes anidados
La contención es una relación real, no una hendidura. droppable y draggable controlan lo que puede ir dentro de qué.
Atributos
Atributos HTML — clase, href, id, datos-*. Acaban apareciendo literalmente en el marcado exportado.
Propiedades
Estado del modelo que no es un atributo HTML. Útil para cualquier cosa que el editor necesite recordar, pero la página no debería llevar.
Rasgos
El panel de ajustes del componente seleccionado. Un rasgo edita un atributo por defecto, o una propiedad cuando configuras changeProp.

Un subárbol típico

  • wrapper, Profundidad 0
  • Section, Profundidad 1
  • Container, Profundidad 2
  • Heading, Profundidad 3
  • Button, Profundidad 3
El Layer Manager muestra exactamente esto. Seleccionar un nodo en el lienzo lo selecciona en el árbol, y viceversa.
components.js
// Every node in the canvas is a Component, and the canvas is a tree.
const wrapper = editor.getWrapper();

wrapper.components().forEach((component) => {
  console.log(
    component.get('type'),      // 'text' | 'image' | 'link' | your own type
    component.getName(),        // label shown in the Layer manager
    component.components().length // number of children
  );
});

// React to what the user selects — the hook most custom UI hangs off.
editor.on('component:selected', (component) => {
  console.log('selected', component.getId(), component.get('type'));
});
getWrapper() es la raíz del árbol. A partir de ahí, components() te da los hijos de cualquier nodo — que es como cada panel personalizado, exportador y validador en un editor de producción recorre el documento.
cta-button.js
// Traits are the settings panel for a component.
// By default a trait writes an HTML attribute.
editor.Components.addType('cta-button', {
  extend: 'link',
  model: {
    defaults: {
      name: 'CTA button',
      attributes: { class: 'btn' },
      components: 'Call to action',
      traits: [
        { name: 'href', label: 'Link' },
        { name: 'title', label: 'Title' },
        {
          type: 'select',
          name: 'target',
          label: 'Opens in',
          options: [
            { id: '', name: 'Same tab' },
            { id: '_blank', name: 'New tab' },
          ],
        },
      ],
    },
  },
});
extender hereda todo de un tipo existente y anula solo lo que nombras. Casi siempre es el punto de partida correcto: un enlace que se comporta como un enlace, con tu propio traits encima.
Principiante

5. Configurar el Style Manager

El Style Manager escribe las reglas de CSS para lo que se seleccione. Dejado en sus valores predeterminados, ofrece una gran porción de CSS a quien use tu editor — lo cual está bien para una herramienta de desarrollo y es incorrecto para casi cualquier producto.

Sectores y lo que suele ir en ellos

Tipografía
familia de fuente, tamaño de fuente, grosor de fuente, altura de línea, color, alineación.
Espaciado
margen y relleno, normalmente las dos propiedades que realmente eligen los autores.
Dimensión
ancho, ancho máximo, altura y sus variantes mínimas/máximas.
Condecoraciones
Color de fondo e imágenes, radio de borde, bordes, sombras.
Responsive
Los estilos se escriben por dispositivo. Cambia el lienzo a una tableta o teléfono y el mismo control escribe en una consulta de medios en su lugar.
Propiedades personalizadas
Puedes definir tus propios tipos de propiedades — un selector de tokens, una escala de espaciado — en lugar de exponer CSS en bruto.
style-manager.js
grapesjs.init({
  container: '#gjs',
  styleManager: {
    // Sectors are the collapsible groups in the right-hand panel.
    // Listing them yourself is how you stop the editor offering
    // 100+ CSS properties to a non-technical author.
    sectors: [
      {
        id: 'typography',
        name: 'Typography',
        open: true,
        properties: [
          'font-family',
          'font-size',
          'font-weight',
          'line-height',
          'color',
          'text-align',
        ],
      },
      { id: 'spacing', name: 'Spacing', properties: ['margin', 'padding'] },
      {
        id: 'dimension',
        name: 'Dimension',
        properties: ['width', 'max-width', 'height'],
      },
      {
        id: 'decorations',
        name: 'Decorations',
        properties: ['background-color', 'border-radius', 'border', 'box-shadow'],
      },
    ],
  },
});
Los sectores son los grupos plegables en el panel derecho. Nombrarlos explícitamente es la forma de convertir "todo CSS" en un conjunto corto y deliberado de elecciones.

La estructura y el diseño son cuestiones distintas

Estructura de componentes

Qué existe, qué contiene qué, qué puede añadirse o eliminarse. Controlado con el tipo de componente, droppable, draggable y removable.

Diseño de componentes

Cómo puede ser un componente. Controlado con la configuración Style Manager y con stylable / unstylable en el propio componente.

restricted-heading.js
// Structure and styling are separate concerns. A component can accept
// children while refusing to be restyled beyond a fixed allowance.
editor.Components.addType('brand-heading', {
  extend: 'text',
  model: {
    defaults: {
      name: 'Brand heading',
      // Only these properties reach the Style manager for this component.
      stylable: ['color', 'text-align'],
      // Everything else stays on the class in your own stylesheet.
      attributes: { class: 'brand-h2' },
    },
  },
});
Un componente puede aceptar niños mientras se niega a ser rediseñado. Esta combinación — estructura abierta, estilo cerrado — es de lo que está hecho un editor de sistemas de diseño.
Principiante

6. Gestionar imágenes y recursos

El Asset Manager es el modal que se abre cuando un lector hace doble clic en una imagen. Lista los recursos, acepta subidas y devuelve una URL al componente seleccionado.

Lo que cubre

Subir imágenes
Arrastrar y soltar o selector de archivos, publicados en un punto final que elijas.
URLs de imágenes
Los lectores pueden pegar una URL de cualquier cosa que ya alojes.
Selección de recursos
Al hacer doble clic en un componente de imagen, se abre el panel y se escribe el src elegido.
Proveedores personalizados
Sustituye la subida por completo por uploadFile y habla con el almacenamiento que uses.
Almacenamiento externo
S3, R2, Cloudinary, un DAM interno — GrapesJS nunca necesita saber dónde están los bytes, solo la URL.
asset-manager.js
grapesjs.init({
  container: '#gjs',
  assetManager: {
    // Seed the panel with images you already host.
    assets: [
      'https://cdn.example.com/hero.jpg',
      { src: 'https://cdn.example.com/team.jpg', name: 'Team', category: 'People' },
    ],
    // Your upload endpoint. Set `upload: false` to disable uploading entirely.
    upload: 'https://api.example.com/uploads',
    uploadName: 'files',
    headers: { Authorization: 'Bearer <token>' },
    multiUpload: true,
    // Add the response's assets to the panel automatically. Your endpoint must
    // answer with { data: [ ...assets ] }.
    autoAdd: true,
  },
});
La subida es la ruta más rápida: apunta a un punto final que responda con { data: [ ... ] } y pon autoAdd. encabezados es donde va tu token de autenticación.
asset-upload.js
// Full control: upload wherever you like, then hand the URLs back.
grapesjs.init({
  container: '#gjs',
  assetManager: {
    async uploadFile(event) {
      const files = event.dataTransfer
        ? event.dataTransfer.files
        : event.target.files;

      const urls = await uploadToYourStorage(files); // S3, R2, Cloudinary…
      editor.AssetManager.add(urls);
    },
  },
});

// Without `upload` or `uploadFile`, dropped images are embedded as base64
// straight into the project — convenient in a demo, painful in production.
uploadFile te entrega los archivos en bruto y se aparta del camino. Esta es la versión que acaban teniendo la mayoría de los editores de producción, porque las subidas suelen requerir firma, redimensionar o un prefijo de tenido.

Una cosa que hay que decidir pronto

Sin ni subida ni uploadFile configurados, las incrustaciones GrapesJS dejaron imágenes en el proyecto como base64. Funciona al instante y infla el proyecto almacenado hasta que se vuelve lento de cargar y caro de mover. Conecta el Asset Manager a un almacenamiento real antes de que alguien empiece a producir contenido.

Intermedio

7. Guardar y cargar proyectos GrapesJS

El Storage Manager decide hacia dónde va el proyecto editable. Está activado por defecto y escribe en localStorage — por eso un editor que construiste ayer todavía tiene el lienzo de ayer.

Lo que te da

Proyecto JSON
Todo el documento editable — componentes, estilos, páginas, recursos — como un objeto serializable y simple.
Guardado automático
Guarda después de un número determinado de ediciones en lugar de en cada pulsación de tecla, con stepsBeforeSave.
Carga
Busca un proyecto en init, o llama tú mismo a loadProjectData() cuando quieras.
Salvar
Activado por autosave, o por editor.store() desde tu propio botón de barra de herramientas.
Almacenamiento remoto
Una tienda integrada basada en búsqueda: dale una URL de carga, una URL de guardado, cabeceras y adaptadores de solicitud/respuesta.
Almacenamiento personalizado
Registra tu propio par de carga/almacenamiento para GraphQL, una caché offline o cualquier cosa remota que no cubra.
storage-manager.js
grapesjs.init({
  container: '#gjs',
  storageManager: {
    type: 'remote',
    autosave: true,
    autoload: true,
    // Batch changes: save after N edits rather than after every keystroke.
    stepsBeforeSave: 5,
    options: {
      remote: {
        urlLoad: '/api/projects/42',
        urlStore: '/api/projects/42',
        headers: { 'X-CSRF-Token': csrfToken },
        credentials: 'include',
        // Shape the request body to match your API…
        onStore: (data) => ({ project: data }),
        // …and pull the project back out of your response.
        onLoad: (result) => result.project,
      },
    },
  },
});
onStore y onLoad son los dos ganchos que importan en un API real: permiten que la carga útil del editor y el contrato de tu endpoint difieran sin que ninguna de las partes comprometa.
custom-storage.js
// When `remote` does not fit — GraphQL, a queue, an offline-first cache —
// register a storage of your own and select it by name.
editor.Storage.add('my-api', {
  async load() {
    const res = await fetch('/api/projects/42');
    const { project } = await res.json();
    return project; // the object you previously stored
  },
  async store(data) {
    await fetch('/api/projects/42', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ project: data }),
    });
  },
});

// storageManager: { type: 'my-api' }

// You can also drive it by hand, with no storage configured at all:
const project = editor.getProjectData();   // plain JSON — store it anywhere
editor.loadProjectData(project);           // and put it back
O salta el Storage Manager por completo. getProjectData() devuelve el JSON simple y loadProjectData() lo vuelve a poner — muchos editores de producción hacen exactamente esto y controlan el guardado desde su propio estado de aplicación.

Dónde termina GrapesJS

GrapesJS proporciona el editor y la capa de datos del proyecto, pero tu aplicación decide dónde se almacenan los datos de producción. Qué usuario es propietario de un proyecto, a qué tenant pertenece, quién puede abrirlo, cuántas revisiones conservas, cuándo está respaldado — nada de eso está en la biblioteca, y nada debería estarlo.

Intermedio

8. Exportar HTML y CSS

Dos llamadas convierten el lienzo en una página. Son las llamadas a las que están conectados los botones HTML y CSS en el editor en vivo de arriba.
export.js
const html = editor.getHtml();
const css = editor.getCss();

// Two things surprise everyone on their first export:
//
// 1. getHtml() returns the canvas wrapped in <body> … </body>.
//    Strip or template around it before you save a fragment.
// 2. getCss() includes GrapesJS's own canvas reset unless you opt out:
const pageCss = editor.getCss({ avoidProtected: true });

// Export one branch instead of the whole page:
const selected = editor.getSelected();
const partial = editor.getHtml({ component: selected });

// The editable project — NOT the same thing as the exported page.
const project = editor.getProjectData();
getHtml() y getCss() leen el documento actual. Ninguno toca el almacenamiento, y ninguno se ve afectado por si has configurado uno.

Dos cosas que sorprenden a todos

getHtml() se envuelve en <body>

El componente del envoltorio se exporta como un elemento del cuerpo. Si guardas un fragmento, la plantilla alrededor o lo desmontas — no asumas que recuperas una sección desnuda.

getCss() incluye el reinicio del editor

GrapesJS incluye una pequeña hoja de estilo protegida para el lienzo. Pasar avoidProtected: cierto cuando solo quieres el CSS que realmente creó tu lector.

Una cadena típica de publicación

  1. GrapesJS
  2. HTML + CSS
  3. Your API
  4. Storage / CMS
  5. Published page
La pipeline exacta es específica de la aplicación: algunos productos escriben un archivo estático, otros almacenan un documento renderizado junto al proyecto, y algunos renderizan el lado del servidor desde el proyecto JSON en cada solicitud. La parte de GrapesJS termina en la primera flecha.

Dos salidas, dos empleos

getHtml() y getCss() te dan lo que ven los visitantes. getProjectData() te da lo que el autor puede seguir editando. Guarda el proyecto; regenera la página a partir de él. Almacenar solo HTML significa que la siguiente edición empieza con el marcado analizado en lugar del documento que el autor ha construido.

Intermedio

9. Extiende GrapesJS con plugins

Los plugins son una de las principales formas de extender GrapesJS más allá del editor principal. Un plugin no es más que una función que recibe la instancia del editor — cualquier cosa que puedas hacer en tu propio código de configuración, un plugin también puede hacerlo.
plugins.js
import grapesjs, { usePlugin } from 'grapesjs';
import blocksBasic from 'grapesjs-blocks-basic';
import forms from 'grapesjs-plugin-forms';

grapesjs.init({
  container: '#gjs',
  plugins: [
    usePlugin(blocksBasic, { flexGrid: true }),
    usePlugin(forms, {}),
  ],
});
usePlugin() registra un plugin con sus opciones. Los tutoriales antiguos muestran grapesjs.plugins.add(); que API está obsoleto en las versiones actuales y muestra una advertencia indicándote que uses este en su lugar.
my-plugin.js
// A plugin is just a function that receives the editor.
// Anything you can do at init you can do inside one.
export default function dividerPlugin(editor, options = {}) {
  const category = options.category ?? 'Basic';

  editor.Blocks.add('divider', {
    label: 'Divider',
    category,
    content: '<hr class="divider" />',
  });

  editor.Commands.add('clear-canvas', {
    run: (ed) => ed.Components.clear(),
  });
}

// Then: plugins: [usePlugin(dividerPlugin, { category: 'Layout' })]
Escribir uno es el mismo trabajo que configurar el editor, movido a un archivo que puedes reutilizar entre proyectos. Bloques, tipos de componentes, comandos, paneles, traits y sectores de estilo pueden registrarse desde dentro.

O instalar uno

¿Necesitas funcionalidades que no estén incluidas en tu editor base? Explora los plugins y extensiones disponibles a través de GJS.Market. A continuación tienes listados reales del catálogo, agrupados por el paso al que pertenecen.

Mercado

Plugins para los pasos que acabas de terminar

Listados reales del catálogo GJS.Market, agrupados por la parte del tutorial que extienden. Nada aquí reemplaza el núcleo — cada uno llena una junta que el núcleo deja deliberadamente abierta.

Avanzado

10. Construir componentes personalizados

Aquí es donde un editor GrapesJS deja de ser un editor HTML genérico y empieza a formar parte de tu producto. Un tipo de componente personalizado es un objeto con nombre que tus autores pueden colocar, con su propia estructura, sus propios ajustes y sus propias reglas sobre lo que se puede cambiar.

Qué se va en un tipo de componente

El tipo
Un nombre que registras con Components.addType, que opcionalmente extiende un tipo incorporado.
El modelo
Predeterminados, hijos, atributos, propiedades y los candados — droppable, draggable, removable, stylable.
Rasgos
La configuración que tu autor realmente ve. changeProp escribe en el modelo en lugar de en un atributo.
La vista
Opcional. Anulación de renderizado cuando el lienzo necesita mostrar algo distinto al marcado exportado.
isComponent
Cómo GrapesJS reconoce tu tipo cuando analiza HTML guardado de nuevo en el árbol.
Un bloque
La entrada de la paleta que lo crea, con contenido: { type: 'your-type' }.

Una tarjeta de producto, como tipo de componente

  • Product Card, Profundidad 0
  • Image, Profundidad 1
  • Product Name, Profundidad 1
  • Price, Profundidad 1
  • CTA, Profundidad 1
El autor edita la imagen, el nombre, el precio y el botón. No pueden borrar el precio, ordenar las piezas o convertir la tarjeta en otra cosa — porque droppable es falso y los hijos están arreglados.
product-card.js
editor.Components.addType('product-card', {
  // Lets GrapesJS recognise the type when parsing saved HTML.
  isComponent: (el) => el.dataset?.gjsType === 'product-card',

  model: {
    defaults: {
      name: 'Product card',
      attributes: { 'data-gjs-type': 'product-card', class: 'product-card' },

      // Author-visible settings. `changeProp` writes to the model
      // instead of to an HTML attribute.
      traits: [
        { name: 'sku', label: 'SKU', changeProp: true },
        {
          type: 'checkbox',
          name: 'showPrice',
          label: 'Show price',
          changeProp: true,
        },
      ],
      sku: '',
      showPrice: true,

      // Fixed structure: the author edits the parts, not the layout.
      components: [
        { type: 'image', attributes: { class: 'product-card__image' } },
        { type: 'text', name: 'Name', components: 'Product name' },
        { type: 'text', name: 'Price', attributes: { class: 'product-card__price' }, components: '$0.00' },
        { type: 'cta-button', components: 'Add to cart' },
      ],

      // Locks that make the card a card and not a free-form div.
      droppable: false,
      stylable: ['background-color', 'border-radius', 'box-shadow'],
    },

    init() {
      this.on('change:showPrice', this.togglePrice);
    },

    togglePrice() {
      const price = this.components().at(2);
      price?.addStyle({ display: this.get('showPrice') ? 'block' : 'none' });
    },
  },
});
Los candados en la parte inferior son la mitad interesante. droppable: false impide que la tarjeta se convierta en un contenedor; stylable limita el restyling a tres propiedades; el traits da al autor exactamente dos decisiones.

Por qué esto es importante para un producto

Una aplicación SaaS puede exponer componentes específicos de producto — una tabla de precios conectada a planes reales, una tarjeta de producto vinculada a un SKU, un widget de reserva — en lugar de un editor genérico y sin restricciones de HTML. Los autores tienen menos opciones y mejores resultados, y tu cola de soporte nunca ve una página rota por un flotador suelto.

Lo estoy montando

Crea un editor controlado con tu sistema de diseño

Los pasos 3, 5 y 10 se combinan en lo más útil que puedes hacer con GrapesJS: reemplazar "todo es posible" por "estas siete cosas, hechas correctamente". Cada mecanismo es uno que ya has conocido.

Bloques personalizados

La paleta es el menú. Si no está en la estantería, nadie puede añadirlo.

Componentes personalizados

Estructura fija por bloque, con las partes que deberían ser editables marcadas como editables.

Rasgos

Los ajustes que recibe un autor — un encabezado, un enlace, una variante — en lugar de marcado en bruto.

Configuración Style Manager

Sectores que solo listan las propiedades que tu sistema realmente permite.

Estilos permitidos

stylable y unstylable por componente, así que una tarjeta puede cambiar de color pero no convertirse en un float.

Componentes reutilizables

Piezas compartidas que se mantienen sincronizadas en lugar de copiarse por página.

Plantillas

Un documento inicial por tipo de página, para que nadie empiece con un lienzo en blanco.

UI personalizado

Los paneles de GrapesJS son reemplazables. Un editor de producto rara vez se parece al predeterminado.

Tu sistema de diseño SaaS

  • Hero
  • Feature Grid
  • Pricing
  • Testimonials
  • FAQ
  • CTA
  • Footer
Siete bloques, cada uno respaldado por un tipo de componente que controlas. Un autor elige de esta estantería y no puede producir una página fuera de marca, porque no hay nada fuera de marca que elegir.

Convierte GrapesJS de un editor genérico en uno diseñado específicamente para tu producto.

Dos guías van más allá en este tema, en dos direcciones:

Integración

Usando GrapesJS con tu framework

GrapesJS se renderiza en un elemento DOM simple, así que "integrarlo con un framework" se reduce a una pregunta: qué gancho de ciclo de vida llama a init() y cuál llama a destroy(). El patrón que aparece a continuación es React; las guías dedicadas cubren el resto, incluyendo las partes que son realmente específicas de cada framework.

Editor.tsx
import { useEffect, useRef } from 'react';
import grapesjs, { type Editor } from 'grapesjs';

export function GjsEditor() {
  const ref = useRef<HTMLDivElement>(null);
  const editorRef = useRef<Editor | null>(null);

  useEffect(() => {
    if (!ref.current) return;
    editorRef.current = grapesjs.init({
      container: ref.current,
      height: '100vh',
      storageManager: false,
    });
    // GrapesJS owns this node now — React must never render into it again.
    return () => {
      editorRef.current?.destroy();
      editorRef.current = null;
    };
  }, []);

  return <div ref={ref} />;
}
Las dos reglas que sobreviven a todo framework: inicializar una vez y nunca dejar que el framework se vuelva a renderizar en el contenedor después. GrapesJS es el propietario de ese nodo.

Una advertencia que afecta a todos en un framework renderizado por servidor: GrapesJS toca la ventana cuando carga el módulo, así que el editor tiene que importarse solo en el cliente. La guía de Next.js cubre exactamente eso.

Arquitectura

Del tutorial a la producción

Un editor de producción suele necesitar más que grapesjs.init(). No porque la biblioteca esté incompleta — porque un editor es una capa de un producto, y las capas que lo rodean son tuyas.

Una pila de producción típica

  1. Your application
  2. Authentication
  3. GrapesJS editor
  4. Custom components / blocks
  5. Storage API
  6. Database / CMS
  7. Asset storage
  8. Publishing
GrapesJS ocupa una banda de este diagrama. Todo lo que está encima y debajo es código de aplicación que escribes, compras o ya tienes.

Lo que tienen que responder las capas circundantes

Autenticación
¿Quién está editando y cómo lo demuestra la llamada de almacenamiento del editor?
Permisos
¿Quién puede editar qué página y quién puede publicarla?
Propiedad del proyecto
A qué usuario o equipo pertenece un proyecto y qué ocurre cuando se va.
Multi-inquilina
Mantener los proyectos, recursos y plantillas de un cliente alejados de los de otro.
Guardado automático
Con qué frecuencia, qué ocurre en una partida fallida y qué ve el lector cuando falla.
Plantillas
Desde dónde empieza una página nueva y cómo llega un cambio de plantilla a las páginas ya creadas.
Almacenamiento de recursos
Dónde van las subidas, cómo se nombran y quién puede recogerlas.
Publicación
Cómo los HTML y CSS exportados se convierten en una página que un visitante puede cargar.
Gestión de errores
Un guardado que falla en silencio es el peor error que puede tener un editor.
Suplentes
Project JSON es pequeño y se comprime bien. No hay excusa para perderlo.
Seguridad
Los bloques de código personalizados y HTML pegados son entradas del usuario. Desinfecta al salir.
Rendimiento
Los proyectos grandes, las listas de recursos extensas y las pilas largas de deshacer tienen un coste que merece la pena medir.
Versión
Revisiones, borradores y la posibilidad de revertir una página que alguien rompió.
La línea divisoria

Responsabilidades de GrapesJS frente a las de tu aplicación

Nada de lo que sigue es una crítica a GrapesJS — es un framework de editores, y este es el lugar correcto para que se detenga. Conocer la línea antes de empezar es lo que evita que una compilación de tres semanas se convierta en una de nueve meses.

Tu aplicación
  • AutenticaciónNo en la biblioteca, por diseño.
  • Roles y permisosQuién puede editar, quién puede publicar.
  • Propiedad del proyectoUsuarios, equipos, transferencia, eliminación.
  • Multi-inquilinaAislamiento entre clientes.
  • VersiónBorradores, revisiones, retrocesos.
  • PublicaciónConvertir una exportación en una página en vivo.
  • SuplentesRetención y restauración.
  • Alojamiento y dominiosDNS, certificados, entrega.
Núcleo GrapesJS
  • Lienzo de ediciónEl iframe, la selección, el paso del curso, las barras de herramientas.
  • Arrastrar y soltarMover, anidar y reordenar componentes.
  • Árbol de componentesTipos, hijos, atributos, traits.
  • Style ManagerEscribir las reglas de CSS para la selección.
  • Edición responsivaDispositivos y estilos por punto de ruptura.
  • Deshacer y volver a hacerComandos básicos, asignados por el teclado por defecto.
  • Datos del proyectoSerializando y restaurando el documento.
  • Exportación HTML/CSSgetHtml() y getCss().
Un plugin o tu código de configuración
  • Biblioteca de bloquesEl núcleo no incluye ninguno: un preset, un plugin o el tuyo.
  • Edición de texto enriquecidoUn mínimo de naves RTE; cambio de CKEditor/TinyMCE/Froala.
  • Pipeline de recursosEl panel se envía; el almacenamiento detrás de él no.
  • Adaptador de almacenamientonave local y remota; tu API es tuyo.

Ocho de estos son tuyos directamente. Esa es la forma honesta de la obra, y es la misma forma que elijas el editor visual.

Elige tu camino

¿Qué estás construyendo?

El núcleo es el mismo para todos ellos. Lo que difiere es la capa que lo rodea — y cada uno de ellos tiene su propia guía.

Evita estos

Errores comunes en GrapesJS

Cada una de estas cosas proviene del mismo lugar: tratar al editor como el producto en lugar de como una capa única.

  1. Confundiendo bloques con componentes

    Acabas añadiendo comportamiento a una entrada de paleta y preguntándote por qué no hace nada una vez que el objeto está en el lienzo.

    Haz esto en su lugar

    Un bloque solo crea cosas. Todo comportamiento — traits, bloqueos, renderizado, validación — pertenece al tipo de componente que crea.

  2. Almacenar todo en estado del navegador

    El almacenamiento predeterminado escribe en localStorage. Parece que se guarda hasta que un lector cambia de dispositivo, borra su navegador o abre el mismo proyecto en dos pestañas.

    Haz esto en su lugar

    Decide dónde están realmente los proyectos antes de que alguien produzca contenido, y pon storageManager: false hasta que lo hayas hecho.

  3. No configurar el almacenamiento en absoluto

    El trabajo persiste silenciosamente en una tienda que nunca diseñaste, y el orden de carga se vuelve impredecible una vez que existen varios proyectos.

    Haz esto en su lugar

    Configura el almacenamiento remoto, registra uno personalizado o guarda el disco y carga con getProjectData() y loadProjectData().

  4. Crear un tipo de componente para cada cosa

    Cuarenta tipos casi idénticos, cada uno con su propio traits, y una paleta que nadie puede navegar.

    Haz esto en su lugar

    Prefiero un tipo con traits antes que cinco tipos que difieran por un color. Extiende los tipos integrados en lugar de reconstruirlos.

  5. Dejando el Style Manager completamente abierto

    Los autores optan por float, posicionamiento absoluto y márgenes de 13px, y cada página se aleja más del sistema de diseño.

    Haz esto en su lugar

    Lista tus sectores explícitamente y usa stylable / unstylable por componente. Menos controles producen mejores páginas.

  6. Tratar a GrapesJS como un CMS completo

    Semanas perdidas buscando usuarios, roles, flujos de trabajo y funciones de publicación que nunca estuvieron ahí.

    Haz esto en su lugar

    Lee primero la división de responsabilidades en el paso 12. GrapesJS es la capa de edición; el CMS que la rodea es tu producto.

  7. No planificar el almacenamiento de recursos

    Sin la subida configurada, las imágenes se incrustan como base64 y el proyecto almacenado crece hasta que carga lenta y es incómodo moverse.

    Haz esto en su lugar

    Conecta el Asset Manager a un almacenamiento real desde el primer día, aunque ese almacenamiento sea una carpeta en el disco.

  8. No definir un flujo de trabajo de publicación

    Tienes un editor que guarda y no hay respuesta a "¿cómo se convierte esto en una página que un visitante puede abrir?".

    Haz esto en su lugar

    Haz un boceto de la pipeline desde el paso 8 antes de construir el editor. Normalmente cambia lo que almacenas.

  9. Poner todas las personalizaciones en un solo plugin

    Un único archivo de 2.000 líneas que registra bloques, tipos, paneles y comandos, y que no puede ser reutilizado ni probado en piezas.

    Haz esto en su lugar

    Un plugin por preocupación. Componen, y a cada uno se le pueden dar opciones.

  10. Ignorar comportamientos receptivos hasta el final

    Páginas que parecen directamente en el lienzo del escritorio y se rompen en un teléfono, con cientos de reglas solo para escritorio ya escritas.

    Haz esto en su lugar

    Cambia de dispositivo mientras construyes. Los estilos se escriben por dispositivo, así que crear a un ancho se hornea ese ancho.

Resolución de problemas

Problemas comunes

Las cinco cosas que más probablemente fallarán en una primera construcción, y qué comprobar en cada una.

El editor no aparece

Normalmente es un problema de montaje más que un problema de GrapesJS.

Comprueba

  • El elemento contenedor existe en el DOM en el momento en que init() se ejecuta.
  • El contenedor tiene una altura — un elemento de altura cero genera un editor de altura cero.
  • La hoja de estilo GrapesJS se carga; sin ella, el editor está presente pero es invisible.
  • La inicialización se ejecuta en el cliente, no durante el renderizado del servidor.

Faltan estilos o el editor parece roto

Hay dos hojas de estilo diferentes y es fácil cargar ninguna.

Comprueba

  • grapesjs/dist/css/grapes.min.css se carga para el propio Chrome del editor.
  • El CSS de tu página se pasa al lienzo — es un iframe, así que la hoja de estilos de tu app no llega automáticamente a él.
  • El Style Manager tiene sectores configurados; un array de sectores vacíos renderiza un panel vacío.
  • El componente seleccionado no está marcado como unstylable para la propiedad que buscas.

El proyecto no ahorra

Escucha storage:error — GrapesJS informa de fallos en lugar de aceptarlos.

Comprueba

  • storageManager está configurado y el tipo coincide con un almacenamiento que realmente está registrado.
  • urlStore es accesible y devuelve un estado de éxito.
  • Se envían credenciales y cabeceras — el almacenamiento remoto predetermina las credenciales para incluir.
  • No hay error CORS en el panel de red; un prevuelo bloqueado parece exactamente un fallo silencioso.

En React, el editor duplica o muere al volver a renderizar

Casi siempre es un problema del ciclo de vida, no uno de GrapesJS.

Comprueba

  • init() se ejecuta una vez, en un efecto con un array de dependencias vacío.
  • destroy() se ejecuta en la limpieza — React 18 Strict Mode monta efectos dos veces en desarrollo.
  • React nunca renderiza hijos en el elemento contenedor después de init().

La compilación o el servidor se cierran al importarlos

GrapesJS toca la ventana cuando se evalúa el módulo, por lo que no puede importarse durante el renderizado del servidor.

Comprueba

  • El componente editor se carga solo en el lado del cliente — importación dinámica con SSR desactivado, o una importación dentro de un efecto.
  • La hoja de estilos no se importa en un módulo renderizado por el servidor.
Próximos pasos

Sigue aprendiendo GrapesJS

A dónde ir una vez que el editor anterior tenga sentido, más o menos en orden de dificultad.

¿Tutorial o referencia?

Esta página es la construcción: instalar, configurar, extender, enviar. La guía completa es la referencia — la arquitectura, el ecosistema y la razón detrás del diseño. La mayoría de la gente acaba leyendo ambos, en ese orden.

Lee la guía completa
Servicios

¿Necesitas ayuda para construir un editor GrapesJS?

La mayor parte de este tutorial es un día de trabajo. La capa debajo — almacenamiento, arrendamiento, publicación, un editor que se ajuste a tu sistema de diseño — es donde los proyectos se hacen largos. GJS.Market puede asumir esa parte.

  • Componentes personalizados
  • Plugins personalizados
  • Creadores de páginas SaaS
  • Migraciones
  • Integraciones
  • Editores de marca blanca
  • Almacenamiento e integración con API
  • Integración con React / Next.js / Vue / Angular
  • Arquitectura de producción
FAQ

Preguntas frecuentes

¿Qué es GrapesJS?

GrapesJS es un framework de creación web de código abierto: un editor visual de arrastrar y soltar que incrustas en tu propia aplicación. Te da un lienzo, un árbol de componentes, un gestor de estilos, un gestor de recursos y un paso de exportación, y deja cuentas, almacenamiento y publicación a la aplicación que lo rodea.

¿GrapesJS es gratis?

Sí. GrapesJS es gratuito para descargar y usar, incluso comercialmente. No hay cuota de licencia, ni número de asientos ni servicio alojado que tengas que comprar — lo gestionas tú mismo. Se pueden pagar plugins opcionales de un marketplace; el editor en sí no.

¿Es GrapesJS de código abierto?

Sí. El núcleo se publica bajo la licencia BSD-3-Clause, y el wrapper oficial de React @grapesjs/react bajo MIT. Ambos permiten uso comercial y modificación. El código fuente está en GitHub y el paquete en npm.

¿Cómo instalo GrapesJS?

O bien npm instala grapesjs en un proyecto con un paso de compilación, o dos etiquetas de un CDN en un archivo HTML simple. Ambas te dan la misma librería; la ruta de CDN no necesita ninguna herramienta. Recuerda cargar la hoja de estilo además del script: sin ella el editor se renderiza pero parece roto.

¿Cómo creo mi primer editor GrapesJS?

Añade un elemento vacío a tu página y luego llama a grapesjs.init({ container: '#gjs' }). Eso es realmente todo lo que se necesita. En la práctica también quieres altura, fromElement: false para que el editor no adopte el marcado existente, y storageManager: false hasta que hayas decidido dónde se ubicarán los proyectos.

¿Qué es un bloque GrapesJS?

Un Bloque es una entrada en la paleta desde la que un usuario arrastra. Contiene una etiqueta, una categoría, un icono y el contenido a crear. No tiene un comportamiento propio: solo produce Componentes. El núcleo GrapesJS no incluye bloques en absoluto: los escribes o añades un plugin preestablecido.

¿Qué es un componente GrapesJS?

Un Componente es un nodo en el lienzo: un modelo con un tipo, atributos, estilos, hijos y traits. El lienzo es un árbol de Componentes, y el HTML exportado se genera a partir de ese árbol en lugar de al revés.

¿Cómo creo un componente personalizado?

Llama a editor.Components.addType('my-type', { model, view }), extendiendo opcionalmente un tipo incorporado. El modelo contiene los valores predeterminados, hijos, traits y los bloqueos — droppable, stylable, removable — que deciden qué puede cambiar un autor. Añade isComponent para que GrapesJS reconozca el tipo al analizar HTML guardado.

¿Cómo añado bloques personalizados?

editor.Blocks.add('my-block', { label, category, media, content }). el contenido toma una cadena HTML o una definición de componente como { type: 'my-type' }. La forma de objeto es la que usas una vez que tienes tus propios tipos de componentes.

¿Cómo guardo proyectos GrapesJS?

Configura el Storage Manager con tipo: 'remoto' y carga/almacena URLs, registra un almacenamiento personalizado con editor.Storage.add() o sáltalo por completo y llama a getProjectData() y loadProjectData() desde tu propio código. El almacenamiento está activado por defecto y escribe en localStorage, que rara vez es lo que quieres en producción.

¿Cómo exporto HTML y CSS?

editor.getHtml() y editor.getCss(). Dos cosas que debes saber: getHtml() devuelve el lienzo envuelto en un elemento corporal, y getCss() incluye el reinicio protegido de lienzo propio de GrapesJS a menos que pases avoidProtected: cierto.

¿Puedo usar GrapesJS con React?

Sí. Inicialízalo en un efecto con un array de dependencias vacío, destrúyelo en la limpieza y no deja que React se renderice nunca más en el contenedor. También hay un envoltorio oficial, @grapesjs/react, por si prefieres componer el UI del editor como componentes React.

¿Puedo usar GrapesJS con Next.js?

Sí, con una salvedad: GrapesJS toca la ventana en el ámbito del módulo, así que debe cargarse solo en el cliente — una importación dinámica con el renderizado del servidor desactivado, o una importación dentro de un efecto. Todo lo demás es igual que el React simple.

¿Puedo usar GrapesJS con Vue o Angular?

Sí. GrapesJS renderiza en un elemento DOM simple, así que funciona con cualquier framework: llama a init() en el gancho de montaje y destroy() en el gancho de desmontaje. No hay un involucrador oficial Vue o Angular — la integración es de unas pocas líneas en cualquier dirección.

¿Puedo crear un creador de páginas SaaS con GrapesJS?

Sí, y es una de las razones más comunes para elegirlo. GrapesJS proporciona la capa de edición; tu aplicación proporciona cuentas, permisos, arrendamiento, almacenamiento, plantillas y publicaciones. Saber esa división antes de empezar es la diferencia entre un proyecto corto y uno largo.

¿Puedo extender GrapesJS con plugins?

Sí. Un plugin es una función que recibe la instancia del editor, así que puede registrar bloques, tipos de componentes, comandos, paneles, traits y sectores de estilo. Registra los plugins con usePlugin(); la antigua API grapesjs.plugins.add() está obsoleta.

¿Dónde puedo encontrar plugins GrapesJS?

Los plugins oficialmente mantenidos están disponibles en npm bajo la organización GrapesJS. GJS.Market cataloga plugins comunitarios y comerciales por categoría — bloques, componentes, almacenes, recursos, editores de texto enriquecido, presets y herramientas para desarrolladores — y todos los listados de esta página provienen de él.
Te toca

¿Listo para construir con GrapesJS?

Empieza con el editor principal, personalízalo para tu producto y amplíalo con plugins e integraciones cuando necesites más funcionalidad.

Empieza aquí

Empezar el tutorial

Instala GrapesJS y ten un editor abierto en los próximos diez minutos.

Ve al paso 1
Ampliación

Explorar plugins

Bloques, componentes, almacenes y proveedores de recursos del catálogo GJS.Market.

Explorar plugins
Construye con nosotros

Construir con nuestro equipo

Componentes personalizados, integración de almacenamiento y arquitectura de producción, hechos contigo.

Háblanos

Todas las muestras de esta página se han comprobado con GrapesJS 0.23.6 en 2026-09-03.