Back

Génération automatique d'écrans squelettes avec boneyard

Génération automatique d'écrans squelettes avec boneyard

Les chargeurs squelettes (skeleton loaders) font partie de ces patterns d’interface qui semblent simples jusqu’à ce qu’on les implémente réellement. On mesure les éléments du DOM, on code en dur des hauteurs, on met en place du rendu conditionnel — puis le designer modifie la mise en page d’une carte et il faut tout recommencer. Le coût de maintenance s’accumule discrètement.

boneyard-js adopte une approche différente : au lieu d’écrire l’interface squelette à la main, l’outil extrait les données de mise en page directement depuis vos composants rendus, au moment du développement, et génère automatiquement les définitions de placeholders.

Points clés à retenir

  • boneyard-js génère automatiquement les chargeurs squelettes en capturant les données de mise en page de vos composants réels, ce qui élimine la nécessité de maintenir deux représentations parallèles de la même interface.
  • L’analyse du DOM s’effectue au moment du développement via Playwright, produisant des fichiers .bones.json statiques. Aucune traversée du DOM n’a lieu en production.
  • La capture s’exécute par défaut sur trois largeurs de viewport (375, 768, 1280 px), avec les valeurs horizontales stockées en pourcentages pour un comportement responsive.
  • La prop fixture et l’option --wait gèrent les composants qui dépendent de données asynchrones indisponibles lors de la capture.
  • Un plugin Vite maintient les bones synchronisés automatiquement à chaque mise à jour HMR, supprimant le besoin d’une étape CLI distincte.
  • Des adaptateurs existent pour React, Vue, Svelte 5, Angular, Preact et React Native, partageant tous le même format de base.

Le problème des chargeurs squelettes manuels

La plupart des implémentations de chargeurs squelettes sont déconnectées de l’interface réelle. Vous construisez le composant réel, puis vous construisez séparément un squelette qui en approxime la forme. Lorsque le composant évolue, le squelette dérive. Avec le temps, vos états de chargement cessent de correspondre à votre contenu, provoquant des décalages de mise en page et des incohérences visuelles.

Des bibliothèques comme react-loading-skeleton réduisent le boilerplate, mais elles vous obligent toujours à décrire manuellement la structure. Vous maintenez encore deux représentations du même composant.

Comment boneyard-js génère automatiquement les écrans squelettes

boneyard-js inverse le flux de travail. Vous enveloppez votre composant réel dans une balise <Skeleton>, vous exécutez une commande CLI, et l’outil capture la mise en page pour vous.

Voici à quoi cela ressemble en React :

import { Skeleton } from 'boneyard-js/react'

function ActivityPanel() {
  const { data, isLoading } = useFetch('/api/activity')

  return (
    <Skeleton name="activity" loading={isLoading}>
      {data && <ActivityContent data={data} />}
    </Skeleton>
  )
}

Ensuite, avec votre serveur de développement en cours d’exécution :

npx boneyard-js build

La CLI ouvre un navigateur headless via Playwright, visite votre application, trouve chaque élément <Skeleton name="..."> et parcourt l’arborescence du DOM à l’intérieur. Elle utilise getBoundingClientRect() sur les nœuds feuilles — éléments textuels, images, boutons, champs de formulaire — et enregistre leur position et leur taille relativement à la racine du squelette. Les valeurs horizontales sont stockées en pourcentages pour un comportement responsive, tandis que les valeurs verticales sont stockées en pixels. Le rayon des bordures est détecté automatiquement.

Ce processus s’exécute par défaut sur trois largeurs de viewport (375, 768, 1280 px), produisant un fichier .bones.json par composant :

{
  "breakpoints": {
    "375": {
      "bones": [
        { "x": 0, "y": 32, "w": 43.59, "h": 34, "r": 8 },
        { "x": 43.59, "y": 39, "w": 23.76, "h": 33, "r": 999 }
      ]
    }
  }
}

Un fichier de registre (registry.js ou registry.ts) est également généré. Importez-le une fois au point d’entrée de votre application, et chaque définition de squelette devient disponible globalement :

import './bones/registry'

À l’exécution, le composant <Skeleton> lit les données de bones enregistrées pour son name, restitue la mise en page correspondante sous forme de rectangles positionnés en absolu, et les remplace par le contenu réel lorsque loading passe à false.

Capture au moment du build, pas d’analyse à l’exécution

Une distinction importante : l’analyse du DOM se produit pendant le développement, pas en production. Les fichiers .bones.json sont des artefacts statiques versionnés avec votre code source. À l’exécution, boneyard-js ne fait que lire ces définitions pré-générées. Il n’y a pas de traversée du DOM en direct dans le navigateur.

Pour React Native, le mécanisme de capture diffère. Le composant <Skeleton> analyse l’arbre de fibres en mode développement à l’aide d’UIManager, mesure les vues natives, et envoie ces données à la CLI. Dans les builds de production, ce code d’analyse est totalement exclu.

Gestion du contenu dynamique et de la prop fixture

Si votre composant dépend d’une API qui n’est pas disponible pendant la capture par la CLI, le squelette peut être généré incorrectement parce que la zone de contenu est vide. Deux options résolvent ce problème :

Utilisez --wait pour différer la capture après le chargement de la page :

npx boneyard-js build --wait 2000

Utilisez la prop fixture pour fournir des données fictives statiques spécifiquement pour la capture :

<Skeleton
  name="activity"
  loading={isLoading}
  fixture={<ActivityContent data={mockData} />}
>
  {data && <ActivityContent data={data} />}
</Skeleton>

Le contenu de fixture n’est rendu que pendant la capture par la CLI et n’a aucun effet en production.

Attention : Si votre composant ne rend rien lorsque data est undefined, l’élément wrapper s’effondre à une hauteur nulle et le squelette ne s’affichera pas. Définissez une minHeight sur le <Skeleton> pour éviter cela.

Plugin Vite pour une intégration plus poussée

Pour les projets basés sur Vite, vous pouvez vous passer entièrement du terminal CLI distinct :

// vite.config.ts
import { defineConfig } from 'vite'
import { boneyardPlugin } from 'boneyard-js/vite'

export default defineConfig({
  plugins: [boneyardPlugin()]
})

Les bones sont capturés au démarrage du serveur de développement et recapturés automatiquement à chaque mise à jour HMR. Cela maintient vos chargeurs squelettes générés au build synchronisés avec votre interface, sans la moindre intervention manuelle.

Prise en charge des frameworks

boneyard-js fournit des adaptateurs spécifiques aux frameworks sous forme d’exports de package distincts :

FrameworkImport
Reactboneyard-js/react
Vueboneyard-js/vue
Svelte 5boneyard-js/svelte
Angularboneyard-js/angular
Preactboneyard-js/preact
React Nativeboneyard-js/native

La logique d’extraction principale et le format .bones.json sont partagés entre tous.

boneyard-js vaut-il la peine d’être adopté ?

boneyard-js est un outil relativement récent, attendez-vous donc à voir évoluer son API. Cela dit, le concept de base est solide : générer l’interface squelette à partir de données de mise en page réelles plutôt que de la maintenir à la main.

L’avantage pratique est plus évident sur les projets où les composants changent fréquemment. Au lieu de mettre à jour les placeholders squelettes après chaque itération de design, vous réexécutez une seule commande. Les fichiers .bones.json se mettent à jour, et vos états de chargement frontend restent fidèles.

Si vous passez déjà du temps à maintenir vos chargeurs squelettes synchronisés avec votre interface réelle, l’automatisation justifie le coût d’installation.

Conclusion

Les chargeurs squelettes ne devraient pas dériver des composants qu’ils représentent, et pourtant les implémentations manuelles le font presque toujours. boneyard-js résout ce problème en traitant les squelettes comme un artefact généré plutôt qu’écrit à la main. L’étape de capture s’exécute en développement, la sortie est un fichier JSON statique, et le coût à l’exécution est minimal. Pour les équipes qui itèrent rapidement sur l’interface, ce flux de travail fait gagner un temps réel et garde les états de chargement visuellement fidèles aux composants sous-jacents.

FAQ

Aucune analyse du DOM ne se produit en production. La CLI ou le plugin Vite génère des fichiers .bones.json statiques au moment du développement. En production, le composant Skeleton lit ces définitions et restitue des rectangles positionnés en absolu, ajoutant seulement un coût d'exécution minime.

Il capture les bones par défaut sur trois largeurs de viewport : 375, 768 et 1280 pixels. Les positions et largeurs horizontales sont stockées en pourcentages afin que le squelette s'adapte fluidement entre les breakpoints, tandis que les valeurs verticales restent en pixels pour un espacement prévisible. À l'exécution, le breakpoint le plus proche est sélectionné en fonction de la largeur actuelle du viewport.

Vous avez deux options. L'option --wait diffère la capture après le chargement de la page pour laisser le temps aux requêtes asynchrones de se résoudre. Alternativement, la prop fixture accepte une version simulée de votre composant alimentée avec des données statiques, qui n'est rendue que pendant la capture par la CLI. Les deux approches garantissent que le squelette reflète une mise en page peuplée plutôt qu'un conteneur vide.

Oui, et vous devriez le faire. Les fichiers .bones.json et registry.js sont des artefacts statiques qui décrivent vos mises en page squelettes. Les versionner garde votre équipe alignée, rend les builds reproductibles sans devoir exécuter l'étape de capture en CI, et permet à la revue de code d'identifier les changements de mise en page inattendus en même temps que les modifications de composants qui les ont causés.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay