Communitygithub.com

divinevideo/divine-web

Rendre un composant de page directement à la racine du sous-domaine (/) sans redirection d'URL dans les SPA React Router.

Qu'est-ce que divine-web ?

Cette compétence résout le problème des redirections URL indésirables dans les applications React Router SPA lorsqu'on utilise des sous-domaines (par exemple, `username.example.com`). Au lieu de rediriger l'utilisateur vers `/profile/npub1...` (ce qui modifie l'URL et casse l'effet "site personnel" propre), elle permet au composant de s'afficher directement à la racine du sous-domaine. Le pattern s'appuie sur un worker en périphérie (Edge Worker) qui injecte les données de l'utilisateur dans le HTML via une variable globale (`window.__SUBDOMAIN_USER__` ou `window.__DIVINE_USER__`). Le composant React utilise alors ces données injectées comme solution de repli lorsque `useParams()` ne trouve aucun paramètre de route (ce qui arrive à la racine `/`). Ce pattern fonctionne avec n'importe quel worker périphérique (Fastly Compute, Cloudflare Workers, etc.) et nécessite une coordination entre le worker côté serveur et le code client pour garantir un rendu correct, sans erreur de paramètre manquant, et avec une URL propre.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Rendu à la Racine d'un Sous-domaine SPA Sans Redirection

Problème

Lors de la construction de routage basé sur des sous-domaines (par exemple, username.example.com), l'utilisation de <Navigate to="/profile/username" replace /> de React Router modifie l'URL, ce qui va à l'encontre de l'objectif d'avoir une URL de sous-domaine propre. De plus, le composant cible peut afficher une page blanche s'il dépend de useParams() pour obtenir des paramètres de route qui n'existent pas à /.

Contexte / Conditions de Déclenchement

  • La page de profil du sous-domaine redirige vers /profile/npub1... au lieu de servir le contenu à /
  • La page apparaît blanche (aucun contenu ne se charge) à la racine du sous-domaine
  • Le composant utilise useParams() et échoue lorsqu'il est rendu sur une route sans ces paramètres
  • Le worker périphérique injecte les données utilisateur dans window.__SUBDOMAIN_USER__ ou une variable globale similaire
  • On souhaite que username.domain.com/ se rende comme un site web personnel

Solution

1. Routeur : Rendre le Composant Directement au Lieu de Naviguer

Avant (provoque une redirection + un changement d'URL) :

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

Après (rend à / sans changement d'URL) :

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

2. Composant : Ajouter une Source de Données de Repli

Le composant doit vérifier les données injectées du sous-domaine lorsque les paramètres de route sont manquants.

Avant (vérifie uniquement les paramètres de route) :

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

  if (!identifier) {
    return <Error message="Aucun identifiant utilisateur fourni" />;
  }
  // ... reste du composant
}

Après (utilise les données injectées comme solution de repli) :

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

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

  // Le paramètre de route a la priorité, les données du sous-domaine sont le repli
  const identifier = npub || subdomainUser?.npub;

  if (!identifier) {
    return <Error message="Aucun identifiant utilisateur fourni" />;
  }
  // ... reste du composant
}

3. Worker Périphérique : Injecter les Données Utilisateur dans le HTML

Le worker périphérique (Fastly Compute, Cloudflare Workers, etc.) doit injecter les données utilisateur :

// Dans le worker périphérique gérant le sous-domaine
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. Accès aux Données Côté Client

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

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

Vérification

  1. Visitez username.domain.com/ - l'URL doit rester à /, pas de redirection
  2. Le contenu du profil doit se charger correctement
  3. Vérifiez la console du navigateur pour l'absence d'erreurs concernant les identifiants manquants
  4. Vérifiez que window.__DIVINE_USER__ contient les données attendues dans les outils de développement

Exemple

Pour divine-web :

  • thecomedianriley.divine.video/ rend ProfilePage à /
  • ProfilePage vérifie useParams() en premier (vide à /)
  • Utilise getSubdomainUser()?.npub comme solution de repli à partir des données injectées
  • Le profil se charge sans changement d'URL

Remarques

  • Ce pattern nécessite une coordination entre le worker périphérique et le code client
  • Le worker périphérique doit injecter les données avant l'hydratation de la SPA
  • Vérifiez toujours les paramètres de route en premier pour que les routes normales /profile/:npub fonctionnent
  • Pensez au SEO : le worker périphérique doit également définir les balises meta appropriées
  • Les liens sur le sous-domaine peuvent utiliser le composant SmartLink pour rester locaux pour le contenu du propriétaire, mais lier vers le domaine racine pour le contenu des autres utilisateurs

Références

Skills associés