---
order: 3
section: guides
title: Modelo de contenido
description: >-
  Archivos, frontmatter, campos integrados, redirecciones y lo que verifica
  scribe validate.
noindex: false
canonicalPath: /es/docs/content-model
---
## Archivos

Un documento equivale a un archivo `.mdx` (o `.md`) en la carpeta del tipo:

```text
content/
  blog/
    hello-world.mdx        → slug "hello-world"
    _redirects.json        → reglas de redirección para este tipo (opcional)
  authors/
    jane.mdx               → slug "jane"
```

- El **nombre del archivo es el slug en inglés**. Los slugs van en minúsculas y usan kebab-case.
- En el disco solo existen los archivos en inglés; las versiones localizadas se guardan en la base de datos SQLite.
- Los archivos cuyo nombre empieza por `_` o mayúscula (por ejemplo, `PUBLISHING.md`) se ignoran, lo que te permite guardar notas junto a tu contenido.

## Frontmatter

El frontmatter usa YAML y se valida mediante el esquema de Zod del tipo:

```mdx
---
title: "Hello, world"
description: "Una descripción lo suficientemente larga para el esquema."
author: jane
publishedAt: "2026-01-15"
updatedAt: "2026-02-01"
---

Body is MDX.
```

La validación se rige por tu esquema de Zod. Si usas un `z.object` normal, las claves desconocidas se eliminan en silencio; usa `.strict()` si prefieres que los errores tipográficos en los nombres de los campos detengan la validación.

## Campos integrados

Estos campos están disponibles en **todos** los tipos de contenido sin necesidad de declararlos en el esquema. Se extraen del frontmatter antes de ejecutar tu esquema, por lo que declararlos en Zod no tiene ningún efecto:

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `publishedAt` | Fecha ISO | Fecha de publicación. |
| `updatedAt` | Fecha ISO | Última actualización importante. Por defecto es `publishedAt`. Controla el `lastModified` del sitemap. |
| `noindex` | boolean | Excluye el documento del sitemap. Puedes exponerlo como etiqueta meta robots en tus páginas. |
| `canonicalPath` | string | Sobrescribe manualmente la ruta de la URL canónica. |

Los documentos localizados heredan `publishedAt`, `updatedAt`, `noindex` y `canonicalPath` del documento original en inglés. Los traductores no pueden modificarlos.

Todo documento cargado incluye también `slug`, `enSlug` (el slug original en inglés, que equivale a `slug` en los documentos en dicho idioma), `locale`, `frontmatter` y `content`.

## Tipos sin cuerpo

Las colecciones que solo sirven como referencia (un tipo `model` o `category` cuyo esquema es totalmente estructural) no suelen tener cuerpo MDX. Para hacerlo explícito, declara `body: false` en el tipo:

```ts
defineContentType({
  id: "model",
  contentDir: "models",
  schema: modelSchema,
  body: false, // las entradas solo tienen frontmatter
});
```

El valor por defecto es `body: true`, lo que garantiza la compatibilidad con versiones anteriores. En un tipo `body: false`, cualquier contenido en el cuerpo que no sea espacio en blanco generará un error de validación, el cargador omitirá el análisis MDX y las exportaciones estáticas no incluirán cuerpo. Un tipo sin cuerpo y con **cero** campos traducibles queda excluido de todos los flujos de traducción (evitando avisos innecesarios de contenido faltante u obsoleto). Si el tipo sin cuerpo conserva algún campo traducible, se mantendrá en las tareas pendientes con una carga que solo incluirá el frontmatter. Consulta la sección de [traducción](/docs/translation) para ver las reglas sobre qué es traducible.

## Más allá del frontmatter

Los cuerpos MDX permiten hacer más que escribir texto simple:

- Los [tokens integrados](/docs/inline-tokens) insertan enlaces, URLs de recursos y literales no traducibles directamente en el cuerpo, resolviéndose por idioma en el momento de lectura.
- Los [recursos](/docs/assets) declarados con `field.asset()` se validan en el disco y se convierten en URLs públicas al cargar.

## Redirecciones: `_redirects.json`

Puedes añadir un archivo `_redirects.json` opcional en cada carpeta de tipo de contenido para declarar migraciones de slugs y documentos retirados. Las redirecciones siguen funcionando aunque elimines el archivo MDX original. Los slugs traducidos de origen se expanden automáticamente desde SQLite.

```json
{
  "redirects": [
    { "from": "hello-world", "toSlug": "hello-scribe" },
    { "from": ["old-a", "old-b"], "toSlug": "hello-scribe" },
    { "from": "moved-post", "toType": "glossary", "toSlug": "virtual-try-on" },
    { "from": "retired-page", "toUrl": "/pricing" },
    { "from": "retired-ext", "toUrl": "https://example.com/app" }
  ]
}
```

Existen tres tipos de redirecciones (solo puede haber un destino por entrada):

| Tipo | Campos | Descripción |
| --- | --- | --- |
| Mismo tipo | `toSlug` | Slug en inglés de destino dentro del mismo tipo de contenido. La URL se crea a partir del `path` del tipo y se localiza por idioma. |
| Otro tipo | `toType` + `toSlug` | Slug en inglés de destino en otro tipo de contenido enrutable (debe tener un `path`). |
| Cualquier destino | `toUrl` | Ruta relativa a la raíz del mismo sitio o URL externa absoluta, idéntica para todos los idiomas. |

- `from`: Solo admite slugs en inglés. Los slugs traducidos de origen se resuelven desde SQLite.
- `permanent` es opcional (su valor por defecto es `true`).

Para retirar o renombrar un documento, añade una entrada a `_redirects.json`, elimina (o renombra) el archivo MDX original, ejecuta `scribe validate` y vuelve a compilar. La función `buildAllContentRedirects()` genera las reglas de redirección para la configuración de tu proxy o framework.

## Validación

```bash
scribe validate
```

Verifica, por cada archivo en inglés, la estructura del esquema, las formas de los campos integrados, tu hook `crossValidate` y la **compilación MDX del cuerpo**. Si un cuerpo no se puede procesar como MDX, se produce un error que bloquea la compilación. Esto aplica tanto a los textos originales en inglés como a las traducciones guardadas.

A nivel de proyecto, también verifica la integridad de las relaciones (una relación requerida huérfana genera un error, mientras que una opcional genera una advertencia), las reglas de `_redirects.json` (destinos válidos, ausencia de orígenes duplicados y sin cadenas de redirección), las reglas de sufijos para slugs localizados, los [campos de recursos declarados](/docs/assets) (existencia de archivos, `formats`, `maxKB`), los [tokens integrados en el cuerpo](/docs/inline-tokens) (etiquetas mal formadas, relaciones huérfanas, enlaces `:href` a tipos no enrutables, claves `vars` faltantes, archivos de recursos faltantes), los tipos `body: false` que incluyen contenido en el cuerpo, los recursos de imagen que faltan si se ha configurado el directorio de recursos y la existencia de la base de datos de traducciones.

El código de salida es distinto de cero si se detecta algún error. Ejecútalo antes de iniciar tu proceso de compilación:

```json
{
  "scripts": {
    "build": "scribe validate && <tu proceso de compilación>"
  }
}
```
