Un portfolio TanStack Start sur GitHub Pages

TanStack Start est un framework full-stack : rendu côté serveur, server functions, routes API. GitHub Pages, lui, ne sait faire qu’une chose — servir des fichiers statiques depuis un CDN. À première vue, les deux ne devraient pas se rencontrer.
Ils se rencontrent quand même, parce que Start sait figer son rendu au build. Le plugin Vite expose une option prerender qui parcourt les routes, exécute le rendu serveur une bonne fois pour toutes, et écrit un fichier HTML par URL. Ce n’est plus un serveur, c’est un dossier.
Le principe : figer le rendu au build
On active le prérendu dans vite.config.ts, et on laisse le crawler suivre les liens internes :
tanstackStart({
prerender: {
enabled: true,
crawlLinks: true,
autoSubfolderIndex: true,
failOnError: true,
},
})crawlLinks extrait les <a href> du HTML généré et enfile les URL trouvées. Comme la page d’accueil pointe vers le blog, et que le blog liste tous les articles, une seule racine suffit à couvrir le site entier. Aucune liste de routes à maintenir à la main : ajouter un article suffit à le faire prérendre.
autoSubfolderIndex écrit /blog/index.html plutôt que /blog.html. C’est exactement ce qu’attend un hébergeur statique pour résoudre /blog sans réécriture.
Le piège du chemin de base
C’est là que la plupart des tentatives se cassent. Un dépôt de projet sur GitHub Pages est servi depuis /mon-repo/, pas depuis la racine. Il faut alors accorder le base de Vite et le basepath du routeur — et même comme ça, les rapports de bugs sur le sujet s’accumulent : assets qui repartent à la racine, routes internes __tsr/* qui ignorent le préfixe.
Plutôt que de se battre avec le préfixe, on le supprime : un dépôt nommé pseudo.github.io est servi à la racine du domaine. Le base reste /, le routeur n’a rien à savoir, et le problème ne se pose plus.
Les deux fichiers qu’on oublie toujours
.nojekyll — sans lui, GitHub Pages fait passer le site par Jekyll, qui ignore silencieusement tout fichier ou dossier commençant par un underscore. Or Start en produit : _shell.html en mode SPA, et le dossier d’assets client selon la configuration. Quand ça arrive, on récupère du HTML nu — aucun style, aucun JS — et rien dans les logs pour l’expliquer. Un fichier vide à la racine règle l’affaire.
404.html — Pages n’a pas de règle de réécriture. Toute URL inconnue tombe sur ce fichier. Ici, la route /404 est prérendue comme les autres, puis un script d’après-build déplace son HTML en 404.html à la racine : le visiteur voit une vraie page 404 du site, avec son en-tête et sa navigation, au lieu de la page d’erreur de GitHub.
Ce qu’on perd, et pourquoi ça ne fait rien
Un site figé n’a plus de server functions, plus de routes API, plus de formulaire qui poste vers son propre backend. Pour un portfolio, ce n’est pas une contrainte : le contenu est écrit en dur, il change quand on commit, et un formulaire de contact se remplace très bien par un lien mailto:.
Ce qu’on garde, c’est l’essentiel : du HTML complet servi au premier octet — donc indexable, donc rapide —, puis une navigation client instantanée une fois le bundle chargé. Hébergement gratuit, TLS inclus, aucun serveur à surveiller.