Communitygithub.com

divinevideo/divine-web

Renderiza un componente de página directamente en la raíz del subdominio (/) sin redirección de URL en aplicaciones SPA con React Router.

¿Qué es divine-web?

Esta habilidad resuelve el problema común en aplicaciones SPA con React Router donde, al usar enrutamiento basado en subdominios (ej. `usuario.ejemplo.com`), la redirección a una ruta como `/perfil/id` cambia la URL y anula el propósito de tener una URL limpia en el subdominio. Además, los componentes que dependen de `useParams()` pueden mostrar una página en blanco al no encontrar parámetros de ruta en `/`. La solución propuesta implica modificar el enrutador para renderizar el componente directamente en `/` sin redirección, y modificar el componente para que use datos inyectados globalmente por un edge worker (como `window.__SUBDOMAIN_USER__`) como fallback cuando los parámetros de ruta están ausentes. Esta habilidad es útil para plataformas que implementan perfiles de usuario en subdominios, como foros, redes sociales o aplicaciones web personales, y es compatible con edge workers como Fastly Compute o Cloudflare Workers. El patrón requiere coordinación entre el edge worker y el código cliente, asegurando que los datos se inyecten antes de la hidratación de la SPA.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/divinevideo/divine-web/tree/HEAD/.agents/skills/spa-subdomain-root-render

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Renderizado en la Raíz del Subdominio SPA Sin Redirección

Problema

Al construir enrutamiento basado en subdominios (ej., usuario.ejemplo.com), usar <Navigate to="/perfil/usuario" replace /> de React Router cambia la URL, lo que va en contra del propósito de tener una URL limpia en el subdominio. Además, el componente destino puede mostrar una página en blanco si depende de useParams() para obtener parámetros de ruta que no existen en /.

Contexto / Condiciones de Activación

  • La página de perfil del subdominio redirige a /perfil/npub1... en lugar de servir contenido en /
  • La página aparece en blanco (sin contenido cargado) en la raíz del subdominio
  • El componente usa useParams() y falla cuando se renderiza en una ruta sin esos parámetros
  • El edge worker inyecta datos de usuario en window.__SUBDOMAIN_USER__ o un global similar
  • Se desea que usuario.dominio.com/ renderice como un sitio web personal

Solución

1. Enrutador: Renderizar el Componente Directamente en Lugar de Navegar

Antes (causa redirección + cambio de URL):

<Route path="/" element={
  subdomainUser
    ? <Navigate to={`/profile/${subdomainUser.npub}`} replace />
    : <Index />
} />

Después (renderiza en / sin cambio de URL):

<Route path="/" element={
  subdomainUser
    ? <ProfilePage />
    : <Index />
} />

2. Componente: Agregar Fuente de Datos de Respaldo

El componente debe verificar los datos inyectados del subdominio cuando faltan parámetros de ruta.

Antes (solo verifica parámetros de ruta):

export function ProfilePage() {
  const { npub } = useParams<{ npub?: string }>();
  const identifier = npub;

  if (!identifier) {
    return <Error message="No se proporcionó identificador de usuario" />;
  }
  // ... resto del componente
}

Después (usa datos inyectados como respaldo):

import { getSubdomainUser } from '@/hooks/useSubdomainUser';

export function ProfilePage() {
  const { npub } = useParams<{ npub?: string }>();
  const subdomainUser = getSubdomainUser();

  // El parámetro de ruta tiene prioridad, los datos del subdominio son el respaldo
  const identifier = npub || subdomainUser?.npub;

  if (!identifier) {
    return <Error message="No se proporcionó identificador de usuario" />;
  }
  // ... resto del componente
}

3. Edge Worker: Inyectar Datos de Usuario en HTML

El edge worker (Fastly Compute, Cloudflare Workers, etc.) debe inyectar los datos del usuario:

// En el edge worker manejando el subdominio
const userData = await fetchUserData(username);
const html = await fetchSpaHtml();

const injectedHtml = html.replace(
  '</head>',
  `<script>window.__DIVINE_USER__ = ${JSON.stringify(userData)};</script></head>`
);

return new Response(injectedHtml, { headers: { 'content-type': 'text/html' } });

4. Acceso a Datos del Lado del Cliente

// useSubdomainUser.ts
interface SubdomainUser {
  subdomain: string;
  pubkey: string;
  npub: string;
  username?: string;
  displayName?: string;
  // ... otros campos
}

export function getSubdomainUser(): SubdomainUser | null {
  if (typeof window === 'undefined') return null;
  return (window as any).__DIVINE_USER__ || null;
}

Verificación

  1. Visita usuario.dominio.com/ - La URL debe permanecer en /, sin redirección
  2. El contenido del perfil debe cargarse correctamente
  3. Verifica en la consola del navegador que no haya errores sobre identificadores faltantes
  4. Verifica que window.__DIVINE_USER__ contenga los datos esperados en las herramientas de desarrollo

Ejemplo

Para divine-web:

  • thecomedianriley.divine.video/ renderiza ProfilePage en /
  • ProfilePage verifica primero useParams() (vacío en /)
  • Usa como respaldo getSubdomainUser()?.npub desde los datos inyectados
  • El perfil se carga sin cambio de URL

Notas

  • Este patrón requiere coordinación entre el edge worker y el código cliente
  • El edge worker debe inyectar los datos antes de la hidratación de la SPA
  • Siempre verifica primero los parámetros de ruta para que las rutas normales /perfil/:npub funcionen
  • Considera SEO: el edge worker también debe establecer las metaetiquetas adecuadas
  • Los enlaces en el subdominio pueden usar el componente SmartLink para mantenerse locales para el contenido del propietario, pero enlazar al dominio principal para el contenido de otros usuarios

Referencias

Skills relacionados