[{"data":1,"prerenderedAt":575},["ShallowReactive",2],{"article-\u002Fblog\u002Fcomposables-nuxt-quand-pourquoi":3},{"id":4,"title":5,"body":6,"brouillon":561,"date":562,"description":563,"extension":564,"image":565,"imageAlt":565,"meta":566,"navigation":152,"path":567,"seo":568,"stem":569,"tags":570,"__hash__":574},"blog\u002Fblog\u002Fcomposables-nuxt-quand-pourquoi.md","Les composables en Nuxt : c'est quoi, quand et dans quels cas",{"type":7,"value":8,"toc":553},"minimark",[9,13,18,49,60,253,260,335,339,342,397,400,404,407,413,437,462,466,469,508,512,522,542,546,549],[10,11,12],"p",{},"« Composable » est l'un de ces mots qui semblent évidents jusqu'au moment où\nil faut décider si un bout de code en mérite un. On finit par en créer trop\npeu (et copier-coller de la logique), ou trop (et transformer chaque\nfonction en composable inutilement). Voici ma façon de trancher.",[14,15,17],"h2",{"id":16},"ce-quest-vraiment-un-composable","Ce qu'est vraiment un composable",[10,19,20,21,25,26,30,31,34,35,34,38,41,42,34,45,48],{},"Un composable est une fonction, par convention préfixée par ",[22,23,24],"code",{},"use",", qui\nencapsule de la ",[27,28,29],"strong",{},"logique réactive"," en s'appuyant sur le système de\nréactivité de Vue (",[22,32,33],{},"ref",", ",[22,36,37],{},"computed",[22,39,40],{},"watch",") et parfois sur le cycle de\nvie des composants (",[22,43,44],{},"onMounted",[22,46,47],{},"onBeforeUnmount",").",[10,50,51,52,55,56,59],{},"La distinction clé est là : une fonction utilitaire classique transforme des\nentrées en sorties et n'a pas de mémoire (",[22,53,54],{},"formatDate(date)","). Un composable,\nlui, ",[27,57,58],{},"détient un état qui vit dans le temps"," et réagit aux changements.",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-typescript shiki shiki-themes github-dark","\u002F\u002F app\u002Fcomposables\u002FuseCompteur.ts\nexport function useCompteur(pasInitial = 1) {\n  const valeur = ref(0)\n  const pas = ref(pasInitial)\n\n  const doubler = computed(() => valeur.value * 2)\n  const incrementer = () => { valeur.value += pas.value }\n  const reinitialiser = () => { valeur.value = 0 }\n\n  return { valeur, doubler, incrementer, reinitialiser }\n}\n","typescript","",[22,68,69,78,110,132,147,154,184,208,233,238,247],{"__ignoreMap":66},[70,71,74],"span",{"class":72,"line":73},"line",1,[70,75,77],{"class":76},"sAwPA","\u002F\u002F app\u002Fcomposables\u002FuseCompteur.ts\n",[70,79,81,85,88,92,96,100,103,107],{"class":72,"line":80},2,[70,82,84],{"class":83},"snl16","export",[70,86,87],{"class":83}," function",[70,89,91],{"class":90},"svObZ"," useCompteur",[70,93,95],{"class":94},"s95oV","(",[70,97,99],{"class":98},"s9osk","pasInitial",[70,101,102],{"class":83}," =",[70,104,106],{"class":105},"sDLfK"," 1",[70,108,109],{"class":94},") {\n",[70,111,113,116,119,121,124,126,129],{"class":72,"line":112},3,[70,114,115],{"class":83},"  const",[70,117,118],{"class":105}," valeur",[70,120,102],{"class":83},[70,122,123],{"class":90}," ref",[70,125,95],{"class":94},[70,127,128],{"class":105},"0",[70,130,131],{"class":94},")\n",[70,133,135,137,140,142,144],{"class":72,"line":134},4,[70,136,115],{"class":83},[70,138,139],{"class":105}," pas",[70,141,102],{"class":83},[70,143,123],{"class":90},[70,145,146],{"class":94},"(pasInitial)\n",[70,148,150],{"class":72,"line":149},5,[70,151,153],{"emptyLinePlaceholder":152},true,"\n",[70,155,157,159,162,164,167,170,173,176,179,182],{"class":72,"line":156},6,[70,158,115],{"class":83},[70,160,161],{"class":105}," doubler",[70,163,102],{"class":83},[70,165,166],{"class":90}," computed",[70,168,169],{"class":94},"(() ",[70,171,172],{"class":83},"=>",[70,174,175],{"class":94}," valeur.value ",[70,177,178],{"class":83},"*",[70,180,181],{"class":105}," 2",[70,183,131],{"class":94},[70,185,187,189,192,194,197,199,202,205],{"class":72,"line":186},7,[70,188,115],{"class":83},[70,190,191],{"class":90}," incrementer",[70,193,102],{"class":83},[70,195,196],{"class":94}," () ",[70,198,172],{"class":83},[70,200,201],{"class":94}," { valeur.value ",[70,203,204],{"class":83},"+=",[70,206,207],{"class":94}," pas.value }\n",[70,209,211,213,216,218,220,222,224,227,230],{"class":72,"line":210},8,[70,212,115],{"class":83},[70,214,215],{"class":90}," reinitialiser",[70,217,102],{"class":83},[70,219,196],{"class":94},[70,221,172],{"class":83},[70,223,201],{"class":94},[70,225,226],{"class":83},"=",[70,228,229],{"class":105}," 0",[70,231,232],{"class":94}," }\n",[70,234,236],{"class":72,"line":235},9,[70,237,153],{"emptyLinePlaceholder":152},[70,239,241,244],{"class":72,"line":240},10,[70,242,243],{"class":83},"  return",[70,245,246],{"class":94}," { valeur, doubler, incrementer, reinitialiser }\n",[70,248,250],{"class":72,"line":249},11,[70,251,252],{"class":94},"}\n",[10,254,255,256,259],{},"Rangé dans ",[22,257,258],{},"app\u002Fcomposables\u002F",", il est auto-importé partout, sans ligne\nd'import :",[61,261,265],{"className":262,"code":263,"language":264,"meta":66,"style":66},"language-vue shiki shiki-themes github-dark","\u003Cscript setup lang=\"ts\">\nconst { valeur, doubler, incrementer } = useCompteur(5)\n\u003C\u002Fscript>\n","vue",[22,266,267,291,326],{"__ignoreMap":66},[70,268,269,272,276,279,282,284,288],{"class":72,"line":73},[70,270,271],{"class":94},"\u003C",[70,273,275],{"class":274},"s4JwU","script",[70,277,278],{"class":90}," setup",[70,280,281],{"class":90}," lang",[70,283,226],{"class":94},[70,285,287],{"class":286},"sU2Wk","\"ts\"",[70,289,290],{"class":94},">\n",[70,292,293,296,299,302,304,307,309,312,315,317,319,321,324],{"class":72,"line":80},[70,294,295],{"class":83},"const",[70,297,298],{"class":94}," { ",[70,300,301],{"class":105},"valeur",[70,303,34],{"class":94},[70,305,306],{"class":105},"doubler",[70,308,34],{"class":94},[70,310,311],{"class":105},"incrementer",[70,313,314],{"class":94}," } ",[70,316,226],{"class":83},[70,318,91],{"class":90},[70,320,95],{"class":94},[70,322,323],{"class":105},"5",[70,325,131],{"class":94},[70,327,328,331,333],{"class":72,"line":112},[70,329,330],{"class":94},"\u003C\u002F",[70,332,275],{"class":274},[70,334,290],{"class":94},[14,336,338],{"id":337},"les-composables-que-nuxt-vous-donne-déjà","Les composables que Nuxt vous donne déjà",[10,340,341],{},"Avant d'écrire le vôtre, sachez que Nuxt en fournit une famille très\nutilisée. Les connaître évite de réinventer :",[343,344,345,354,368,381,389],"ul",{},[346,347,348,353],"li",{},[27,349,350],{},[22,351,352],{},"useState"," : un état réactif partagé et compatible SSR (voir l'article\ndédié à la gestion d'état).",[346,355,356,361,362,367],{},[27,357,358],{},[22,359,360],{},"useFetch"," et ",[27,363,364],{},[22,365,366],{},"useAsyncData"," : récupérer des données avec cache,\ndéduplication et gestion des états de chargement.",[346,369,370,361,375,380],{},[27,371,372],{},[22,373,374],{},"useRoute",[27,376,377],{},[22,378,379],{},"useRouter"," : lire la route courante et naviguer.",[346,382,383,388],{},[27,384,385],{},[22,386,387],{},"useRuntimeConfig"," : accéder à la configuration exposée à l'exécution.",[346,390,391,396],{},[27,392,393],{},[22,394,395],{},"useCookie"," : lire et écrire un cookie de façon réactive.",[10,398,399],{},"Beaucoup de besoins sont déjà couverts. Le composable maison arrive quand\naucun de ceux-là ne répond exactement à votre cas.",[14,401,403],{"id":402},"quand-écrire-un-composable","Quand écrire un composable",[10,405,406],{},"Trois signaux clairs :",[10,408,409,412],{},[27,410,411],{},"1. La même logique réactive apparaît dans plusieurs composants."," Deux\npages qui gèrent un formulaire avec les mêmes règles de validation, deux\nécrans qui écoutent la même source de données : extrayez la logique dans un\ncomposable et les deux la partagent sans duplication.",[10,414,415,418,419,422,423,426,427,34,430,34,433,436],{},[27,416,417],{},"2. Un composant devient gros à cause de sa logique, pas de son template.","\nSi votre ",[22,420,421],{},"\u003Cscript setup>"," dépasse largement votre ",[22,424,425],{},"\u003Ctemplate>",", c'est souvent\nque plusieurs préoccupations cohabitent. Les découper en composables\n(",[22,428,429],{},"useFiltres",[22,431,432],{},"usePagination",[22,434,435],{},"useSelection",") rend chacune testable et\nnommée.",[10,438,439,442,443,446,447,450,451,454,455,457,458,461],{},[27,440,441],{},"3. Vous manipulez une API du navigateur avec du cycle de vie."," Un\nécouteur d'événement, un ",[22,444,445],{},"IntersectionObserver",", un ",[22,448,449],{},"matchMedia"," : tout ce\nqui doit s'installer au montage et se nettoyer à la destruction est un\ncandidat naturel. Le composable ",[22,452,453],{},"useReveal"," de ce site, par exemple,\nencapsule un ",[22,456,445],{}," et son nettoyage : le composant appelant\nignore tout de cette mécanique, il appelle ",[22,459,460],{},"useReveal()"," et c'est réglé.",[14,463,465],{"id":464},"quand-ne-pas-en-écrire-un","Quand ne PAS en écrire un",[10,467,468],{},"Le réflexe inverse est tout aussi important.",[343,470,471,492,502],{},[346,472,473,476,477,480,481,484,485,488,489,491],{},[27,474,475],{},"Une transformation pure, sans état"," : c'est un utilitaire, pas un\ncomposable. ",[22,478,479],{},"formatPrix(montant)"," va dans ",[22,482,483],{},"utils\u002F",", pas dans\n",[22,486,487],{},"composables\u002F",". Le préfixe ",[22,490,24],{}," sur une fonction sans réactivité est un\nfaux signal qui trompe le lecteur.",[346,493,494,497,498,501],{},[27,495,496],{},"Un besoin de structure visuelle réutilisable"," : c'est un composant, pas\nun composable. Si vous voulez réutiliser du markup, écrivez un ",[22,499,500],{},".vue",".",[346,503,504,507],{},[27,505,506],{},"De la logique utilisée à un seul endroit et qui ne grossit pas"," :\nlaissez-la dans le composant. Extraire par principe, alors qu'il n'y a ni\nduplication ni complexité, ajoute un fichier et un niveau d'indirection\npour rien.",[14,509,511],{"id":510},"les-deux-règles-à-ne-pas-violer","Les deux règles à ne pas violer",[10,513,514,515,521],{},"Un composable doit ",[27,516,517,518],{},"s'appeler au niveau supérieur du ",[22,519,520],{},"setup",", jamais dans\nune condition ou une boucle, exactement comme les hooks. C'est ce qui permet\nà Vue d'associer correctement l'état réactif au composant.",[10,523,524,525,531,532,534,535,537,538,541],{},"Et il doit ",[27,526,527,528,530],{},"retourner ses ",[22,529,33],{}," sans les déballer",". Retournez ",[22,533,301],{}," (le\n",[22,536,33],{},"), pas ",[22,539,540],{},"valeur.value"," : sinon le composant appelant reçoit une valeur\nfigée, déconnectée de la réactivité. C'est l'erreur de débutant la plus\nfréquente sur les composables.",[14,543,545],{"id":544},"la-leçon","La leçon",[10,547,548],{},"La bonne question n'est pas « est-ce que je peux en faire un composable »\n(on peut presque toujours), mais « est-ce que ce code détient de l'état\nréactif, et est-ce que je le réutilise ou le sépare pour de bonnes\nraisons ». État réactif plus réutilisation ou clarté : composable. Simple\ntransformation : utilitaire. Structure visuelle : composant. Avec ces trois\ncases, on range juste, et le projet reste lisible.",[550,551,552],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":66,"searchDepth":80,"depth":80,"links":554},[555,556,557,558,559,560],{"id":16,"depth":80,"text":17},{"id":337,"depth":80,"text":338},{"id":402,"depth":80,"text":403},{"id":464,"depth":80,"text":465},{"id":510,"depth":80,"text":511},{"id":544,"depth":80,"text":545},false,"2026-07-12","Un composable n'est pas juste une fonction rangée dans un dossier : c'est le bon endroit pour la logique réactive réutilisable. Quand en écrire un, et quand s'en abstenir.","md",null,{},"\u002Fblog\u002Fcomposables-nuxt-quand-pourquoi",{"title":5,"description":563},"blog\u002Fcomposables-nuxt-quand-pourquoi",[571,572,573],"Nuxt","Composables","Vue","O4wyp0cmi-N7cc0_fb-cXsUpIRcDcZKIc7Xzhrz0TnQ",1784652016702]