Communitygithub.com

divinevideo/divine-web

Rendere eine Seitenkomponente direkt im Subdomain-Stammverzeichnis (/) ohne URL-Weiterleitung in React Router SPAs.

Was ist divine-web?

Diese Fähigkeit löst das Problem, dass Subdomain-basierte Routen (z. B. `username.example.com`) in React Router SPAs (Single Page Applications) den Benutzer auf eine URL wie `/profile/npub1...` umleiten, was die saubere URL-Struktur zerstört. Die Lösung besteht darin, die Komponente direkt unter `/` zu rendern, ohne eine Navigationsumleitung durchzuführen. Dafür wird ein Edge-Worker (z. B. Fastly Compute oder Cloudflare Workers) eingesetzt, der Benutzerdaten in die HTML-Seite injiziert und in `window.__DIVINE_USER__` bereitstellt. Die React-Komponente selbst prüft zunächst auf Route-Parameter (z. B. `useParams()`) und greift bei deren Fehlen (wie auf der Stamm-URL) auf diese globalen Edge-Daten zurück. Dies ermöglicht ein nahtloses Erlebnis, bei dem `username.domain.com/` wie eine persönliche Website aussieht, ohne dass die URL umgeschrieben wird. Die Fähigkeit ist plattformunabhängig und funktioniert mit jedem Edge-Worker, der HTML vor der Auslieferung modifizieren kann, und erfordert eine enge Koordination zwischen Server (Edge) und Client (SPA).

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

SPA Subdomain Root Rendern ohne Weiterleitung

Problem

Beim Aufbau subdomain-basierter Routen (z. B. username.example.com) ändert die Verwendung von React ROuters <Navigate to="/profile/username" replace /> die URL, was den Zweck einer sauberen Subdomain-URL zunichte macht. Zudem kann die Zielkomponente eine leere Seite anzeigen, wenn sie auf useParams() angewiesen ist, um Routenparameter zu erhalten, die unter / nicht existieren.

Kontext / Auslösebedingungen

  • Die Subdomain-Profilseite leitet auf /profile/npub1... weiter, anstatt den Inhalt unter / bereitzustellen
  • Die Seite erscheint leer (kein Inhalt wird geladen) auf dem Subdomain-Stammverzeichnis
  • Die Komponente verwendet useParams() und schlägt fehl, wenn sie auf einer Route ohne diese Parameter gerendert wird
  • Der Edge Worker injiziert Benutzerdaten in window.__SUBDOMAIN_USER__ oder ein ähnliches Global
  • Es soll username.domain.com/ wie eine persönliche Website aussehen

Lösung

1. Router: Komponente direkt rendern statt Navigieren

Vorher (verursacht Weiterleitung + URL-Änderung):

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

Nachher (rendert unter / ohne URL-Änderung):

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

2. Komponente: Fallback-Datenquelle hinzufügen

Die Komponente muss auf injizierte Subdomain-Daten zurückgreifen, wenn Routenparameter fehlen.

Vorher (prüft nur Routenparameter):

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

  if (!identifier) {
    return <Error message="Keine Benutzerkennung bereitgestellt" />;
  }
  // ... Rest der Komponente
}

Nachher (greift auf injizierte Daten zurück):

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

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

  // Routenparam hat Priorität, Subdomain-Daten sind Fallback
  const identifier = npub || subdomainUser?.npub;

  if (!identifier) {
    return <Error message="Keine Benutzerkennung bereitgestellt" />;
  }
  // ... Rest der Komponente
}

3. Edge Worker: Benutzerdaten in HTML injizieren

Der Edge Worker (Fastly Compute, Cloudflare Workers u.ä.) sollte Benutzerdaten injizieren:

// Im Edge Worker, der die Subdomain behandelt
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. Clientseitiger Datenzugriff

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

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

Überprüfung

  1. Besuche username.domain.com/ - die URL sollte bei / bleiben, nicht weiterleiten
  2. Der Profilinhalt sollte korrekt geladen werden
  3. Überprüfe die Browserkonsole auf keine Fehler bezüglich fehlender Kennungen
  4. Verifiziere window.__DIVINE_USER__ in den DevTools enthält die erwarteten Daten

Beispiel

Für divine-web:

  • thecomedianriley.divine.video/ rendert ProfilePage unter /
  • ProfilePage prüft zuerst useParams() (leer bei /)
  • Greift zurück auf getSubdomainUser()?.npub aus den injizierten Daten
  • Profil lädt ohne URL-Änderung

Hinweise

  • Dieses Muster erfordert Koordination zwischen Edge Worker und Client-Code
  • Der Edge Worker muss Daten vor der SPA-Hydration injizieren
  • Immer zuerst Routenparameter prüfen, damit normale /profile/:npub-Routen weiterhin funktionieren
  • SEO beachten: Der Edge Worker sollte auch angemessene Meta-Tags setzen
  • Links auf der Subdomain können die SmartLink-Komponente verwenden, um lokal für den Besitzer zu bleiben, aber für Inhalte anderer Benutzer auf die Apex-Domain zu verlinken

Referenzen

Verwandte Skills