El tipo de campo json de EmDash almacena datos estructurados arbitrarios, pero el editor predeterminado es una entrada de texto de una sola línea donde debe escribir JSON sin formato a mano. Field Kit es un plugin de primera parte que incluye cuatro widgets componibles para campos json, configurados completamente mediante options de seed — sin necesidad de React para los creadores de sitios.
Instalación
npm i @emdash-cms/plugin-field-kit
Registre el plugin en astro.config.mjs:
import { defineConfig } from "astro/config";
import emdash from "emdash";
import { fieldKitPlugin } from "@emdash-cms/plugin-field-kit";
export default defineConfig({
integrations: [
emdash({
plugins: [fieldKitPlugin()],
}),
],
});
Luego adjunte un widget a cualquier campo json configurando widget como field-kit:<name>:
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": { "fields": [...] }
}
Widgets
| Widget | Usar para | Valor almacenado |
|---|---|---|
object-form | Formulario en línea para objetos JSON planos | { key: value, ... } |
list | Editor de array ordenado con agregar / eliminar / reordenar | [{ ... }, ...] |
grid | Matriz de filas × columnas | { rowKey: { colKey: value } } |
tags | Entrada de chip/tag de forma libre | ["tag1", "tag2"] |
Si a un widget le faltan sus options requeridas (por ejemplo, fields para object-form/list, o rows/columns para grid), el editor muestra una advertencia en línea “Widget mal configurado” en lugar de una entrada rota — útil mientras se itera en esquemas de seed.
object-form
Renderiza un grupo de subcampos tipados que se almacenan como un solo objeto JSON. Bueno para datos estructurados de forma fija como datos nutricionales o información de contacto.
{
"slug": "nutrition",
"type": "json",
"widget": "field-kit:object-form",
"options": {
"collapsed": false,
"fields": [
{ "key": "calories", "label": "Calories", "type": "number", "suffix": "kcal" },
{ "key": "protein", "label": "Protein", "type": "number", "suffix": "g" },
{ "key": "fat", "label": "Fat", "type": "number", "suffix": "g" },
{ "key": "carbs", "label": "Carbs", "type": "number", "suffix": "g" }
]
}
}
Valor almacenado: { "calories": 250, "protein": 12.5, "fat": 8, "carbs": 30 }.
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
fields | SubFieldDef[] | (requerido) | Definiciones de subcampos — ver Subcampos. |
collapsed | boolean | false | Renderizar el grupo colapsado por defecto. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
list
Un editor de array ordenado con controles de agregar, eliminar y reordenar. Cada fila es un objeto JSON cuya forma está definida por fields. El encabezado de la fila muestra un resumen renderizado desde una plantilla tipo Mustache.
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": {
"itemLabel": "Ingredient",
"min": 1,
"max": 50,
"sortable": true,
"summary": "{{name}} — {{amount}}",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "amount", "label": "Amount", "type": "text" },
{ "key": "optional", "label": "Optional", "type": "boolean" }
]
}
}
Valor almacenado:
[
{ "name": "Flour", "amount": "500g", "optional": false },
{ "name": "Butter", "amount": "200g", "optional": false }
]
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
fields | SubFieldDef[] | (requerido) | Definiciones de subcampos para cada fila. |
itemLabel | string | "Item" | Etiqueta singular para una fila (usada en el botón “Agregar” y títulos de fila de respaldo). |
min | number | — | Número mínimo de elementos. Por debajo de esto, el botón de eliminar se oculta. |
max | number | — | Número máximo de elementos. En este recuento, el botón de agregar se oculta. |
sortable | boolean | true | Mostrar botones de reordenar arriba/abajo. |
summary | string | — | Plantilla Mustache renderizada como título de fila colapsada. Ver Plantillas de resumen. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
grid
Una matriz bidimensional de filas × columnas. Cada celda puede ser un interruptor, entrada de texto, entrada numérica o selección. Útil para matrices como disponibilidad estacional, tablas de precios o comparaciones de características.
{
"slug": "availability",
"type": "json",
"widget": "field-kit:grid",
"options": {
"cell": "toggle",
"rows": [
{ "key": "berries", "label": "Berries" },
{ "key": "stoneFruit", "label": "Stone fruit" },
{ "key": "citrus", "label": "Citrus" }
],
"columns": [
{ "key": "spring", "label": "Spring" },
{ "key": "summer", "label": "Summer" },
{ "key": "autumn", "label": "Autumn" },
{ "key": "winter", "label": "Winter" }
]
}
}
Valor almacenado:
{
"berries": { "spring": false, "summer": true, "autumn": false, "winter": false },
"stoneFruit": { "spring": false, "summer": true, "autumn": true, "winter": false },
"citrus": { "spring": false, "summer": false, "autumn": true, "winter": true }
}
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
rows | GridAxisDef[] | (requerido) | Definiciones de fila: { key, label, image? }. |
columns | GridAxisDef[] | (requerido) | Definiciones de columna: { key, label, image? }. |
cell | "toggle" | "text" | "number" | "select" | "toggle" | Tipo de entrada de celda, aplicado uniformemente a cada celda. |
cellOptions | string[] | Array<{ label, value }> | [] | Requerido cuando cell es "select". |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
tags
Una entrada tipo chip para arrays de strings. Admite una lista fija de suggestions, valores personalizados de forma libre (conmutable), transformaciones de mayúsculas/minúsculas y un max opcional.
{
"slug": "keywords",
"type": "json",
"widget": "field-kit:tags",
"options": {
"placeholder": "Add a keyword…",
"max": 10,
"transform": "lowercase",
"allowCustom": true,
"suggestions": ["vegan", "vegetarian", "gluten-free", "dairy-free", "nut-free"]
}
}
Valor almacenado: ["vegan", "gluten-free"].
Presione Enter o , para confirmar una etiqueta. Backspace en una entrada vacía elimina la última etiqueta. Las etiquetas duplicadas se ignoran silenciosamente.
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
placeholder | string | "Add..." | Marcador de posición de entrada mostrado cuando no hay etiquetas presentes. |
max | number | — | Número máximo de etiquetas. La entrada se oculta en el límite. |
suggestions | string[] | [] | Sugerencias de autocompletar mostradas mediante una <datalist>. |
allowCustom | boolean | true | Cuando es false, solo se pueden agregar valores de suggestions. |
transform | "none" | "lowercase" | "uppercase" | "trim" | "none" | Normalizar etiquetas a medida que se agregan. |
helpText | string | — | Texto de ayuda mostrado debajo del widget. |
Subcampos
object-form y list aceptan un array options.fields de definiciones de subcampos tipados. Cada entrada tiene una key (la clave del objeto JSON en la que escribe), un label, un type y extras específicos del tipo.
| Tipo de subcampo | Se renderiza como | Extras notables |
|---|---|---|
text | Entrada de una sola línea | placeholder |
textarea | Entrada de varias líneas | rows (predeterminado 3), placeholder |
number | Entrada numérica | min, max, step, prefix, suffix, placeholder |
boolean | Interruptor | — |
select | Desplegable | options: string[] | Array<{ label, value }>, placeholder |
date | Entrada de fecha | — |
color | Selector de color nativo emparejado con entrada de texto hexadecimal | — |
url | Entrada de URL (HTML5 type="url") | placeholder |
Props comunes en cada subcampo: required, helpText, defaultValue.
Plantillas de resumen
El widget list renderiza cada fila colapsada usando una plantilla tipo Mustache en options.summary. {{key}} se reemplaza con el valor de la fila para esa clave (convertido a string). Los valores falsy vuelven a "{itemLabel} {n}".
"summary": "{{name}} — {{amount}}"
Renderiza filas como Flour — 500g. La plantilla es una sustitución de string simple — sin HTML, sin expresiones anidadas.
Durabilidad de datos
Los widgets de Field Kit almacenan JSON simple en la columna existente del campo. No hay tablas específicas del plugin, ni claves foráneas, ni mutación de esquema. Si elimina @emdash-cms/plugin-field-kit de su configuración, los datos permanecen válidos — solo la UI de edición vuelve a la entrada de texto json predeterminada.
Esto se aplica incluso cuando cambia la forma del widget: las claves desconocidas en los objetos almacenados se conservan en la próxima escritura, por lo que puede evolucionar un esquema sin perder datos capturados bajo un conjunto de campos más antiguo.
Ver también
- Descripción general de plugins — cómo funcionan los plugins de EmDash.
- Elegir un formato de plugin — escriba sus propios widgets de campo si Field Kit no se ajusta.
- Discussion #571 — la propuesta que llevó a este plugin.