Ludovic Frank - Développeur indépendant

Turnstile et Turbo, comment on fait ? (un peu d'aide de stimulus ? 😝)

ionicons-v5-k Ludovic Frank 2 sept. 2025
146 lectures Niveau : Confirmé

Eh ! coucou par ici, ça faisait longtemps 😁,

Merci d'avoir cliqué sur cet article 🙂, comme toujours j'espère qu'il vous plaira.

En cette période de rentrée, ça peut être une bonne idée de refaire un petit article, et en plus, ça tombe bien j'ai un truc intéressant à vous dire.

Oui, je ne fais jamais d'article si je trouve que ça n'a pas de valeur, pareil, les articles générés par IA, pas sur ce blog, pas mon truc.

Après je ne dis pas pour d'autres projets... 😝.

Aujourd'hui on va parler de Turnstile avec Hotwired Turbo, vous êtes prêts ? alors, c'est parti.

Pourquoi cet article ?

Actuellement, je travaille sur la refonte de ViteUneTable, vous vous souvenez ? c'est l'application qui utilise Symfony UX Turbo, dont je vous parlais en 2022.

Comme je vous le disais donc, cette application utilise Turbo et Stimulus, et elle fait son petit bonhomme de chemin depuis 2022.

Mais, là j'ai besoin de la modifier afin d'ajouter un modèle "freemium", une version de base gratuite avec des fonctionnalités payantes.

Et pour ça... j'ai besoin d'exposer des formulaires publiquement, et, vous savez ce que ça fait un formulaire public ? Ça se fait spammer.

Les formulaires sur le net

Les bots rôdent et dès qu'ils voient un formulaire à remplir sur le net, ils vont le remplir avec tout un tas de trucs pas très intéressants.

Dans l'histoire d'Internet, il y a toujours eu différentes méthodes pour protéger ces formulaires, par exemple WordPress utilise Akismet pour protéger les formulaires de commentaires.

Il y a également Google avec ReCaptcha, très pratique, il les aide à entraîner leurs modèles de reconnaissance d'images 😁, bon toute blague mise à part la version 3 ne demande plus à l'utilisateur de cliquer sur des images, c'est plutôt transparent (mais lourd pour le navigateur).

Cloudflare Turnstile

Et puis, il y a mon préféré, Cloudflare Turnstile, c'est simple, efficace et ça marche, jamais eu de problème avec, ça leur fait un peu de pub mais ça ne me dérange pas. Vu les services qu'ils rendent, ça peut se faire un peu de pub, sans problème.

Rappel, qu'est-ce que Turbo ?

Turbo est une bibliothèque qui a pour objectif de remplacer une application web classique, qui à chaque clic sur un lien, recharge tout le CSS et le JavaScript sur la page suivante par une application de type "SPA", ce qui veut dire "Single Page Application", à chaque changement d'interface, le contexte ne se recharge pas entièrement, seul l'HTML change.

Il est souvent accompagné d'une autre bibliothèque appelée "Stimulus" qui elle s'occupe des animations plus poussées.

Ces deux bibliothèques ensemble, c'est ce qu'on appelle la stack "Hotwire".

Les différentes problématiques de notre projet

Avant de passer au code,

Turnstile fonctionne très bien avec les sites statiques mais...

Avec Symfony, il y a par exemple un bundle permettant de l'intégrer à vos formulaires, ça fonctionne très bien, vous avez juste à l'ajouter à votre formulaire et il chargera un template qui embarque le JavaScript de Cloudflare Turnstile.

Le problème, c'est que dans notre cas, ce n'est pas un site classique, quand on change de page, on ne recharge pas le JavaScript, seul le "body" et son HTML changent. Et la bibliothèque Cloudflare ne détecte pas qu'elle doit injecter un "widget" Cloudflare pour les formulaires qui ont été injectés sur le DOM par Turbo.

Changer et revenir sur la page

Imaginons qu'on charge correctement Turnstile sur la page où il y a un formulaire, on part de cette page car on change d'avis puis, on revient...

Le problème ? Quand on revient le widget ne sera plus fonctionnel, le JavaScript de Turnstile a déjà été chargé donc il ne cherche pas sur la page de nouveaux formulaires.

Turbo et sa mise en cache

Quand avec Turbo, vous revenez sur une page où vous êtes déjà passé (avec le bouton retour du navigateur ou en recliquant sur le lien), Turbo va brièvement remplir votre page avec ce qu'il a en cache, à savoir le HTML dans l'état dans lequel il était quand vous avez quitté la page.

Si vous avez quelque chose qui surveille ce qui se passe sur le DOM, alors vous allez déclencher le script deux fois, lorsque la prévisualisation de Turbo est appliquée, puis quand il a récupéré le vrai HTML côté serveur.

D'un point de vue optimisation c'est horrible, car Turnstile, il fait des tests dans le navigateur, autrement dit s'il le fait deux fois d'affilée, on consomme une fois pour rien.

Dans un monde sur batterie, ce n'est pas la meilleure des idées.

Éviter les race conditions

Bon, là je chipote un peu et c'est peu probable que ça arrive car qui mettrait deux formulaires avec Turnstile sur la même page (quoique, avec des modals).

Mais dans le monde de Turbo, il est commun d'utiliser un controller Stimulus pour ce genre de cas.

Et un controller Stimulus, il est lié à un élément du DOM, on pourrait le lier à un élément plus vaste du DOM que simplement la "div" où on va charger le widget Turnstile.

Mais ça ne rendrait pas notre contrôleur Stimulus portable facilement sur d'autres projets (et donc cet article aurait beaucoup moins d'intérêt).

Le code : un petit contrôleur Stimulus

Basé sur toutes les observations que je viens de vous donner, voici le code que j'ai mis en place pour régler ce problème.

Le controller Stimulus

Vous voyez que j'utilise beaucoup des "statics", ce qui n'est pas commun (et pas très propre non plus) mais ici ça me permet de mettre en place un code portable qui gère les "race conditions".

Le template

Ici j'utilise les fonctions Twig de Symfony pour déclarer l'utilisation du contrôleur Stimulus, mais ça fonctionnerait tout aussi bien avec de simples data-attributes.

Rien de bien particulier

Conclusion

Et voilà, notre petit article de rentrée touche à sa fin.

On est dans quelque chose d'assez simple pour reprendre mais très utile, vous verrez prochainement comment je l'utilise.

Également prochainement on quittera un peu le monde Symfony et Symfony-UX pour aller sur une autre stack, je ne vous en dis pas plus, vous verrez.

Très bonne semaine à vous.