Playground / Experimento
Notion CMS
La integración técnica detrás de /now. Un Server Component lee directamente la Notion API con ISR. Sin SDK externo, sin script manual, sin MDX intermediario.
Decisiones tomadas: marzo 2026 · Sesión de contenidos.
- Página
- Now
- ID
- 0aa0a698a15f4842898a02f5ca517d84
- Bloques
- 9
- Última edición
- 8/4/2026, 5:13:00
- Sincronizado
- 11/4/2026, 6:09:53
- Revalida cada
- 3600s (1h)
El framework — Burner List de Jake Knapp
/now no es un CV actualizado ni una lista de tareas. Es una foto honesta del estado presente: lo que tiene el foco, lo que está en segundo plano, y el ruido que hay que gestionar. La estructura viene de The Burner List de Jake Knapp.
Una hoja dividida en dos columnas. Izquierda = fuego delantero. Derecha = fuego trasero.
Front burner (columna izquierda) — Un único proyecto. El más importante. Solo uno. Con las tareas concretas que lo mueven en los próximos días. El espacio en blanco es enfoque visible.
Back burner (columna derecha, parte superior) — El segundo proyecto más importante. Consciente pero no protagonista.
Kitchen sink (columna derecha, parte inferior) — Todo lo demás sin categorizar. El fregadero donde va el resto.
La regla más importante: la Burner List es desechable. Se rehace cuando cambia el front burner. El acto de rehacer te obliga a decidir qué sigue siendo importante y qué ya no.
Arquitectura de la integración
La fuente de datos
Una página simple a nivel workspace en Notion (no hija de ningún otro documento, no wiki, no database). Contiene la Burner List directamente como bloques: headings + callouts. Pablo la edita libremente. Sirve como CMS único de /now.
Estructura actual de la página Notion:
## 🔥 Front burner → callout con tareas del proyecto principal
## 🫕 Back burner → callout con el segundo proyecto
## 🪣 Kitchen sink → callout con el resto
## ⚙️ Cómo funciona → toggle con instrucciones (no se sincroniza)El flujo
Pablo edita la página de Notion
↓
Notion API devuelve los bloques
↓
fetchBlocks() los lee con fetch nativo (ISR: revalidate 3600s)
↓
Next.js sirve HTML pre-renderizado
↓
/now actualizado en producción (máx. 1h de latencia)El código
// lib/notion.ts — fetch recursivo (bloques e hijos)
async function fetchBlocks(blockId: string, revalidate: number) {
const res = await fetch(
`https://api.notion.com/v1/blocks/${blockId}/children`,
{
headers: { Authorization: `Bearer ${process.env.NOTION_TOKEN_NOW}` },
next: { revalidate }, // ← ISR automático
}
)
const data = await res.json()
const blocks = data.results ?? []
for (const block of blocks) {
if (block.has_children) {
block.children = await fetchBlocks(block.id, revalidate)
}
}
return blocks
}
// app/now/page.tsx
export const revalidate = 3600
export default async function NowPage() {
const data = await getNowPage() // server-side, cacheado
return <BlocksRenderer groups={groupBlocks(data.blocks)} />
}Decisiones técnicas
Variables de entorno con nombre específico
NOTION_TOKEN_NOW en lugar de NOTION_TOKEN genérico. El nombre genérico asume una única integración Notion. En este proyecto tiene sentido ser específico: la integración de /now es independiente y puede haber otras (writing, work…) con sus propios tokens y permisos. Nombrar NOTION_TOKEN_NOW hace explícita esa separación sin coste adicional.
ISR en lugar de webhook (v1)
La integración funciona con revalidación automática cada hora. Para publicación instantánea (Fase 2), la opción recomendada es un Deploy Hook en Vercel ejecutado con npm run sync-now. El webhook automático de Notion queda documentado pero pendiente — empieza simple, añade complejidad solo si la frecuencia lo justifica.
Trampas conocidas al configurar
- Conectar la página a la integración. Notion no da acceso automático — hay que compartir explícitamente vía
··· → Connections. Si no se hace, la API devuelveobject_not_foundsin más pistas. - Variables en Project Settings, no en Team Settings. Las variables de equipo no se inyectan en los builds. Hay que crearlas siempre en
Project → Settings → Environment Variables. - Redeploy obligatorio tras añadir variables. Vercel no lo dispara automáticamente si no hay push de código.
Incidencias resueltas
Callouts sin contenido interior (28 mar 2026)
Síntoma: /now cargaba los headings (Front burner, Back burner, Kitchen sink) pero los callouts aparecían vacíos.
Causa: La Notion API devuelve callouts con has_children: true pero fetchBlocks solo leía el primer nivel. El renderer de callout pintaba el rich_text del bloque raíz (vacío en este caso), ignorando los bloques hijos.
Fix: fetchBlocks ahora es recursiva — si un bloque tiene has_children: true, fetchea sus hijos y los asigna a block.children. El renderer de callout pinta los hijos con BlocksRenderer.
Fase 2 — Publicación instantánea
Actualmente la sincronización es automática cada 60 minutos vía ISR. Para publicar un cambio de Notion de forma inmediata:
Opción A — Deploy Hook + script npm (recomendada)
Crear un Deploy Hook en Vercel (Settings → Git → Deploy Hooks) y añadir en apps/web/package.json:
"scripts": {
"sync-now": "curl -X POST https://api.vercel.com/v1/integrations/deploy/XXXXXXX"
}Ejecutar con npm run sync-now desde apps/web o desde la raíz con npm run sync-now --workspace=apps/web.
Opción B — Webhook de Notion
Notion detecta el cambio → llama al Deploy Hook automáticamente → Vercel redeploy. Sin intervención manual. Más complejidad de configuración. Estado: pendiente — documentado, no implementado.