[{"data":1,"prerenderedAt":180},["ShallowReactive",2],{"article-\u002Fwriting\u002Fdruxt-0250-caching-and-performance-buffs-20261023":3},{"id":4,"title":5,"articleType":6,"categories":7,"date":11,"description":12,"extension":13,"meta":14,"paragraphs":16,"path":175,"readingTime":176,"sitemap":177,"stem":178,"__hash__":179},"articleEntries\u002Farticles-data\u002Fdruxt-0250-caching-and-performance-buffs-20261023.json","Druxt 0.25.0; caching and performance buffs","Blog post",[8,9,10],"Drupal","Druxt","Planet Drupal","2026-10-23T18:00:00+11:00","Druxt 0.25.0 takes a Nuxt server from 17 requests to Drupal a page to 11, and to none with content caching on, with Drupal's own Cache-Control deciding how long and a webhook to clear it. Plus Manage display changes without a rebuild.","json",{"feedImage":15},true,[17,36,46,66,81,110,122,130,136,148,156,166],{"type":18,"layout":19,"regions":20},"section","layout_onecol",{"content":21},[22,25,27,29],{"type":23,"html":24},"text_formatted","\u003Cp>Druxt is the Fully Decoupled Drupal framework for Nuxt 2, and 0.25.0, the first Druxt release since 2023, makes it ask Drupal a lot less.\u003C\u002Fp>",{"type":23,"html":26},"\u003Cp>On druxtjs.org, the Druxt docs site, a page render made 17 requests to Drupal. On 0.25.0 it makes 11, and with content caching on it makes none, until Drupal's lifetime for the page runs out. The Nuxt server keeps what Drupal sends for as long as Drupal's \u003Ccode>Cache-Control\u003C\u002Fcode> allows, and a webhook clears it when content changes.\u003C\u002Fp>",{"type":23,"html":28},"\u003Cp>Separately, the first-load flicker is gone, and with \u003Ccode>schema.refresh\u003C\u002Fcode> on, a change in Drupal's Manage display shows up without a rebuild, which is the part I have wanted for years. The upgrade command is near the end.\u003C\u002Fp>",{"type":30,"src":31,"alt":32,"caption":33,"width":34,"height":35},"media","\u002Fimages\u002Fwriting\u002Fdruxt-0250-caching-and-performance-buffs-20261023\u002Fdruxt-0-25-0-content-cache-grid.png","Three dot grids, one dot per request to Drupal, one column per page of a 24-page warm crawl. Druxt 0.24.0 makes 17 on most pages and 29 and 35 on the two section pages, 438 in all. Druxt 0.25.0 with cache.content off makes 11 on most pages, 294 in all. Druxt 0.25.0 with cache.content on makes none on any page. Counted at a proxy in front of Drupal answering max-age 300.","Requests to Drupal per page over a 24-page warm crawl: 17 a page on Druxt 0.24.0, 11 on 0.25.0, and 0 on 0.25.0 with cache.content on. Counted at a proxy, Drupal answering max-age 300; each process also reads the index and the decoupled settings once at start.",1280,640,{"type":18,"layout":19,"regions":37,"title":45},{"content":38},[39,41,43],{"type":23,"html":40},"\u003Cp>0.24.0 made 17 calls a page, and six of them were the JSON:API index and the menus, asked again on every page. The index is Drupal's map of its API, and Druxt reads it to find everything else. Two of the six were duplicates within one render, and they go the moment you upgrade: identical requests now share one fetch, and the menu cache keys on the query it sends.\u003C\u002Fp>",{"type":23,"html":42},"\u003Cp>The other four are kept on the Nuxt server between requests. Drupal's \u003Ccode>Cache-Control\u003C\u002Fcode> decides how long, \u003Ccode>ttl\u003C\u002Fcode> can only shorten it, and when in doubt nothing is kept. The \u003Ca href=\"https:\u002F\u002Fdruxtjs.org\u002Fhow-to\u002Fcaching#how-long-it-lasts\">caching guide\u003C\u002Fa> has the full rules. Across a 24-page crawl, that is 144 index and menu requests down to none once the first page has been rendered.\u003C\u002Fp>",{"type":23,"html":44},"\u003Cp>A signed-in request never fills it: an \u003Ccode>Authorization\u003C\u002Fcode> header, basic auth or a Drupal session cookie skips it, and an analytics cookie doesn't count.\u003C\u002Fp>","Asking Drupal once",{"type":18,"layout":19,"regions":47,"title":65},{"content":48},[49,51,57,63],{"type":23,"html":50},"\u003Cp>Index and menu caching is on by default in production builds. Drupal needs one setting, the page cache max-age, which ships at 0. It applies to anonymous responses across the site, so browsers and CDNs will hold pages for five minutes too. Check Drupal itself rather than a CDN in front of it:\u003C\u002Fp>",{"type":30,"src":52,"alt":53,"caption":54,"width":55,"height":56},"\u002Fimages\u002Fwriting\u002Fdruxt-0250-caching-and-performance-buffs-20261023\u002Fdrupal-performance-max-age.png","Drupal 11's Performance page under Administration, Configuration, Development, with the Caching section open and Browser and proxy cache maximum age set to 5 min, above Drupal's note that the value is used as max-age in Cache-Control headers.","Administration > Configuration > Development > Performance, with the page cache maximum age at 5 minutes.",788,551,{"type":58,"title":59,"code":60,"language":61,"highlighted":62},"code","Five minutes, then check the header","drush -y config:set system.performance cache.page.max_age 300\ndrush -y config:export\ncurl -sI https:\u002F\u002Fcms.example.com\u002Fjsonapi | grep -i cache-control\n# cache-control: max-age=300, public","bash","drush \u003Cspan class=\"token parameter variable\">-y\u003C\u002Fspan> config:set system.performance cache.page.max_age \u003Cspan class=\"token number\">300\u003C\u002Fspan>\ndrush \u003Cspan class=\"token parameter variable\">-y\u003C\u002Fspan> config:export\n\u003Cspan class=\"token function\">curl\u003C\u002Fspan> \u003Cspan class=\"token parameter variable\">-sI\u003C\u002Fspan> https:\u002F\u002Fcms.example.com\u002Fjsonapi \u003Cspan class=\"token operator\">|\u003C\u002Fspan> \u003Cspan class=\"token function\">grep\u003C\u002Fspan> \u003Cspan class=\"token parameter variable\">-i\u003C\u002Fspan> cache-control\n\u003Cspan class=\"token comment\"># cache-control: max-age=300, public\u003C\u002Fspan>",{"type":23,"html":64},"\u003Cp>If you have turned CORS on in Drupal's \u003Ccode>services.yml\u003C\u002Fcode>, allow one origin, or \u003Ccode>*\u003C\u002Fcode> without credentials. Two or more origins, or an origin pattern, add \u003Ccode>Vary: Origin\u003C\u002Fcode> to every response, and nothing gets cached. Credentials are fine with one origin, so a preview setup like \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Fconsumer_node_preview\">Consumer Node Preview\u003C\u002Fa>'s keeps caching, and an editor's signed-in preview skips the cache anyway.\u003C\u002Fp>","Turning it on",{"type":18,"layout":19,"title":67,"regions":68},"Caching content",{"content":69},[70,72,77,79],{"type":23,"html":71},"\u003Cp>The index and menus are cached by default. Set \u003Ccode>cache.content\u003C\u002Fcode> and the same cache keeps everything else Druxt fetches from Drupal: entities, collections, views and route lookups.\u003C\u002Fp>",{"type":58,"title":73,"code":74,"language":75,"highlighted":76},"nuxt.config.js, with content caching","export default {\n  druxt: {\n    cache: { content: true, secret: process.env.DRUXT_CACHE_SECRET },\n  },\n}","js","\u003Cspan class=\"token keyword\">export\u003C\u002Fspan> \u003Cspan class=\"token keyword\">default\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n  \u003Cspan class=\"token literal-property property\">druxt\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n    \u003Cspan class=\"token literal-property property\">cache\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan> \u003Cspan class=\"token literal-property property\">content\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token boolean\">true\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"token literal-property property\">secret\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> process\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>env\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>\u003Cspan class=\"token constant\">DRUXT_CACHE_SECRET\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n  \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n\u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>",{"type":23,"html":78},"\u003Cp>On a local copy of druxtjs.org, a warm docs page went from 11 calls to Drupal to none. A cold crawl, every page visited once, went from 438 calls on 0.24.0 to 157, a third of them on the first page, where schema refresh reads Drupal's displays. Only successful anonymous responses are kept, so a signed-in visitor still reaches Drupal, and a page published after someone saw its 404 shows straight away.\u003C\u002Fp>",{"type":23,"html":80},"\u003Cp>Content changes far more often than menus, so pair it with the clear from Drupal, below. Each cache area holds at most 50 MB of stored JSON per consumer by default, set by \u003Ccode>cache.maxSize\u003C\u002Fcode>, and can take about three times that in memory, so size it against your Nuxt process.\u003C\u002Fp>",{"type":18,"layout":19,"regions":82,"title":109},{"content":83},[84,86,90,92,94,98,103,107],{"type":23,"html":85},"\u003Cp>Five minutes is a long time to wait for a menu edit, so Drupal can clear the cache the moment content changes. Give Druxt a secret:\u003C\u002Fp>",{"type":58,"title":87,"code":88,"language":75,"highlighted":89},"nuxt.config.js","export default {\n  druxt: {\n    cache: { secret: process.env.DRUXT_CACHE_SECRET },\n  },\n}","\u003Cspan class=\"token keyword\">export\u003C\u002Fspan> \u003Cspan class=\"token keyword\">default\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n  \u003Cspan class=\"token literal-property property\">druxt\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n    \u003Cspan class=\"token literal-property property\">cache\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan> \u003Cspan class=\"token literal-property property\">secret\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> process\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>env\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>\u003Cspan class=\"token constant\">DRUXT_CACHE_SECRET\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n  \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n\u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>",{"type":23,"html":91},"\u003Cp>A server-rendered production build then has a clear endpoint, \u003Ccode>POST \u002F_druxt\u002Fcache\u002Fclear\u003C\u002Fcode>, locked to the secret in an \u003Ccode>X-Druxt-Secret\u003C\u002Fcode> header. Each Nuxt process keeps its own cache, so a site running several clears each one. List Druxt, or a module that adds it such as \u003Ccode>druxt-site\u003C\u002Fcode>, under \u003Ccode>modules\u003C\u002Fcode> rather than \u003Ccode>buildModules\u003C\u002Fcode>, or the endpoint answers 404 under \u003Ccode>nuxt start\u003C\u002Fcode>.\u003C\u002Fp>",{"type":23,"html":93},"\u003Cp>On druxtjs.org, Drupal's Purge module sends that POST whenever a cache tag is invalidated, so a saved menu link reaches the frontend on the next request. It clears Druxt's cache only, so a CDN in front of Drupal needs its own purger, and a \u003Ccode>drush cr\u003C\u002Fcode> doesn't send it.\u003C\u002Fp>",{"type":58,"title":95,"code":96,"language":61,"highlighted":97},"Purge, sending the clear to Nuxt","drush -y pm:install purge purge_ui purge_purger_http \\\n  purge_queuer_coretags purge_processor_lateruntime\ndrush p:purger-add httpbundled   # note the purger ID it prints\n# At Configuration > Development > Performance > Purge, set the purger to\n# Nuxt's host and port, path \u002F_druxt\u002Fcache\u002Fclear, method POST,\n# invalidation type tag, and a header X-Druxt-Secret.","drush \u003Cspan class=\"token parameter variable\">-y\u003C\u002Fspan> pm:install purge purge_ui purge_purger_http \u003Cspan class=\"token punctuation\">\\\u003C\u002Fspan>\n  purge_queuer_coretags purge_processor_lateruntime\ndrush p:purger-add httpbundled   \u003Cspan class=\"token comment\"># note the purger ID it prints\u003C\u002Fspan>\n\u003Cspan class=\"token comment\"># At Configuration > Development > Performance > Purge, set the purger to\u003C\u002Fspan>\n\u003Cspan class=\"token comment\"># Nuxt's host and port, path \u002F_druxt\u002Fcache\u002Fclear, method POST,\u003C\u002Fspan>\n\u003Cspan class=\"token comment\"># invalidation type tag, and a header X-Druxt-Secret.\u003C\u002Fspan>",{"type":58,"title":99,"code":100,"language":101,"highlighted":102},"settings.php, keeping the secret out of exported config","$config['purge_purger_http.settings.PURGER_ID']['headers'][0]['value'] = getenv('DRUXT_CACHE_SECRET');","php","\u003Cspan class=\"token variable\">$config\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">[\u003C\u002Fspan>\u003Cspan class=\"token string single-quoted-string\">'purge_purger_http.settings.PURGER_ID'\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">]\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">[\u003C\u002Fspan>\u003Cspan class=\"token string single-quoted-string\">'headers'\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">]\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">[\u003C\u002Fspan>\u003Cspan class=\"token number\">0\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">]\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">[\u003C\u002Fspan>\u003Cspan class=\"token string single-quoted-string\">'value'\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">]\u003C\u002Fspan> \u003Cspan class=\"token operator\">=\u003C\u002Fspan> \u003Cspan class=\"token function\">getenv\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">(\u003C\u002Fspan>\u003Cspan class=\"token string single-quoted-string\">'DRUXT_CACHE_SECRET'\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">)\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">;\u003C\u002Fspan>",{"type":58,"title":104,"code":105,"language":61,"highlighted":106},"Check the endpoint, expecting 204","curl -s -o \u002Fdev\u002Fnull -w '%{http_code}\\n' -X POST \\\n  -H \"X-Druxt-Secret: your-secret\" http:\u002F\u002Fnuxt:3000\u002F_druxt\u002Fcache\u002Fclear","\u003Cspan class=\"token function\">curl\u003C\u002Fspan> \u003Cspan class=\"token parameter variable\">-s\u003C\u002Fspan> \u003Cspan class=\"token parameter variable\">-o\u003C\u002Fspan> \u002Fdev\u002Fnull \u003Cspan class=\"token parameter variable\">-w\u003C\u002Fspan> \u003Cspan class=\"token string\">'%{http_code}\\n'\u003C\u002Fspan> \u003Cspan class=\"token parameter variable\">-X\u003C\u002Fspan> POST \u003Cspan class=\"token punctuation\">\\\u003C\u002Fspan>\n  \u003Cspan class=\"token parameter variable\">-H\u003C\u002Fspan> \u003Cspan class=\"token string\">\"X-Druxt-Secret: your-secret\"\u003C\u002Fspan> http:\u002F\u002Fnuxt:3000\u002F_druxt\u002Fcache\u002Fclear",{"type":23,"html":108},"\u003Cp>Every tag invalidation clears the whole cache, which is blunt but cheap. The \u003Ca href=\"https:\u002F\u002Fdruxtjs.org\u002Fhow-to\u002Fcaching#clear-on-save-with-purge\">caching guide\u003C\u002Fa> has the details.\u003C\u002Fp>","Clearing it from Drupal",{"type":18,"layout":19,"regions":111,"title":121},{"content":112},[113,115,119],{"type":23,"html":114},"\u003Cp>Druxt builds its schemas, which fields a component renders and in what order, from Manage display at build time, so moving a field meant a rebuild. With \u003Ccode>schema.refresh\u003C\u002Fcode> on, the server regenerates them after each clear: move a field, save, and the next page has it.\u003C\u002Fp>",{"type":58,"title":116,"code":117,"language":75,"highlighted":118},"nuxt.config.js, with schema refresh","export default {\n  modules: ['druxt-site'], \u002F\u002F or druxt-schema, not buildModules\n  druxt: {\n    cache: { secret: process.env.DRUXT_CACHE_SECRET },\n    schema: { refresh: true },\n  },\n}","\u003Cspan class=\"token keyword\">export\u003C\u002Fspan> \u003Cspan class=\"token keyword\">default\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n  \u003Cspan class=\"token literal-property property\">modules\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">[\u003C\u002Fspan>\u003Cspan class=\"token string\">'druxt-site'\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">]\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"token comment\">\u002F\u002F or druxt-schema, not buildModules\u003C\u002Fspan>\n  \u003Cspan class=\"token literal-property property\">druxt\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan>\n    \u003Cspan class=\"token literal-property property\">cache\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan> \u003Cspan class=\"token literal-property property\">secret\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> process\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>env\u003Cspan class=\"token punctuation\">.\u003C\u002Fspan>\u003Cspan class=\"token constant\">DRUXT_CACHE_SECRET\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"token literal-property property\">schema\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">{\u003C\u002Fspan> \u003Cspan class=\"token literal-property property\">refresh\u003C\u002Fspan>\u003Cspan class=\"token operator\">:\u003C\u002Fspan> \u003Cspan class=\"token boolean\">true\u003C\u002Fspan> \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n  \u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"token punctuation\">,\u003C\u002Fspan>\n\u003Cspan class=\"token punctuation\">}\u003C\u002Fspan>",{"type":23,"html":120},"\u003Cp>The first page after a clear pays for it, with about two dozen configuration reads on druxtjs.org, and every page after that is back to normal. The server generates with the build's access to Drupal, so those credentials need to be in production too. A static site can run the same refresh from its own Node server with \u003Ccode>createSchemaRefresh\u003C\u002Fcode>, which the \u003Ca href=\"https:\u002F\u002Fdruxtjs.org\u002Fhow-to\u002Fcaching#schemas-after-a-display-change\">caching guide\u003C\u002Fa> covers.\u003C\u002Fp>","Display changes without a rebuild",{"type":18,"layout":19,"regions":123,"title":129},{"content":124},[125,127],{"type":23,"html":126},"\u003Cp>Over a network that adds up. On a local copy of druxtjs.org, alternating the versions page by page, 0.25.0 rendered warm pages nearly a third faster than 0.24.0 in all 66 paired visits, and in about a sixth of 0.24.0's time with content caching on. Inside a hosting cluster, where Drupal is a couple of milliseconds away, the time saved is small, and the win is Drupal answering fewer requests. Put a proxy in front of your own Drupal and count: how many times does your site ask the same question?\u003C\u002Fp>",{"type":23,"html":128},"\u003Cp>Drupal is getting quicker too. ptmkenny's core patch in \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Fdrupal\u002Fissues\u002F3626255\">#3626255\u003C\u002Fa> reads JSON:API's cached normalizations in bulk. On the docs collection behind druxtjs.org's sitemap, with normalizations warm, it took 1301 database queries down to 77 with identical responses. It needs reviews, and the issue covers running it on 11.x.\u003C\u002Fp>","What it saves",{"type":18,"layout":19,"regions":131,"title":135},{"content":132},[133],{"type":23,"html":134},"\u003Cul>\u003Cli>No more first-load flicker. Druxt components no longer fetch their data again when a production build hydrates, so any \u003Ccode>components:extend\u003C\u002Fcode> hook you added to hide it can go.\u003C\u002Fli>\u003Cli>Pages don't jump on a refetch, because components keep their content while they fetch.\u003C\u002Fli>\u003Cli>Views follow the language you asked for, not the site default.\u003C\u002Fli>\u003Cli>A 403 or 404 from a route renders through Nuxt's own error page.\u003C\u002Fli>\u003Cli>Vue and Vuex are peer dependencies, so a site uses its own copy, and a mismatched second copy can't stop Nuxt at startup.\u003C\u002Fli>\u003C\u002Ful>","Also in 0.25.0",{"type":18,"layout":19,"regions":137,"title":147},{"content":138},[139,141,145],{"type":23,"html":140},"\u003Cp>The packages version together, so move them in one go:\u003C\u002Fp>",{"type":58,"title":142,"code":143,"language":61,"highlighted":144},"Upgrade the set together","# Drop any druxt package your site doesn't use\nnpm install druxt@^0.25.0 druxt-site@^0.15.0 druxt-router@^0.32.0 \\\n  druxt-entity@^0.29.0 druxt-menu@^0.22.0 druxt-blocks@^0.18.0 \\\n  druxt-views@^0.23.0 druxt-breadcrumb@^0.18.0 druxt-schema@^0.12.0 \\\n  axios@^0.34.0\n# Still on Vue 2.6? Add vue@^2.7.14 vuex@^3.6.2\n# Using druxt-auth? Take it to 0.5.1 in the same install.","\u003Cspan class=\"token comment\"># Drop any druxt package your site doesn't use\u003C\u002Fspan>\n\u003Cspan class=\"token function\">npm\u003C\u002Fspan> \u003Cspan class=\"token function\">install\u003C\u002Fspan> druxt@^0.25.0 druxt-site@^0.15.0 druxt-router@^0.32.0 \u003Cspan class=\"token punctuation\">\\\u003C\u002Fspan>\n  druxt-entity@^0.29.0 druxt-menu@^0.22.0 druxt-blocks@^0.18.0 \u003Cspan class=\"token punctuation\">\\\u003C\u002Fspan>\n  druxt-views@^0.23.0 druxt-breadcrumb@^0.18.0 druxt-schema@^0.12.0 \u003Cspan class=\"token punctuation\">\\\u003C\u002Fspan>\n  axios@^0.34.0\n\u003Cspan class=\"token comment\"># Still on Vue 2.6? Add vue@^2.7.14 vuex@^3.6.2\u003C\u002Fspan>\n\u003Cspan class=\"token comment\"># Using druxt-auth? Take it to 0.5.1 in the same install.\u003C\u002Fspan>",{"type":23,"html":146},"\u003Cp>The deprecated \u003Ccode>DruxtField\u003C\u002Fcode> components are off unless you set \u003Ccode>druxt.entity.components.fields: true\u003C\u002Fcode>, and a \u003Ccode>consola\u003C\u002Fcode> 2.15.3 pin can go. Wrapper components no longer render module data as HTML attributes, so \u003Ccode>entity=\"[object Object]\"\u003C\u002Fcode> is gone from your markup. Wrappers still read it from \u003Ccode>$attrs\u003C\u002Fcode>. The rest is in the \u003Ca href=\"https:\u002F\u002Fdruxtjs.org\u002Fapi\u002Fpackages\u002Fdruxt\u002FCHANGELOG\">changelog on druxtjs.org\u003C\u002Fa>.\u003C\u002Fp>","Upgrading",{"type":18,"layout":19,"title":149,"regions":150},"Why 0.25.0, and not Nuxt 4?",{"content":151},[152,154],{"type":23,"html":153},"\u003Cp>Nuxt 2 is end of life, so it is a fair question. Druxt had years of improvements and fixes sitting unreleased, and I would rather finish them than carry them across half done. 0.25.0 gets that work out, and gives the 2.0.0 port a stable, fast baseline to start from. Get it right on the stack it runs on today, then upgrade, then add new features, then polish.\u003C\u002Fp>",{"type":23,"html":155},"\u003Cp>There is one more 0.x release planned before 1.0.0. 0.26.0 is mostly waiting on a \u003Ca href=\"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Fdecoupled_router\u002Fissues\u002F3111456\">decoupled router patch\u003C\u002Fa>, so a site whose default language has no URL prefix stops redirecting its front page to \u003Ccode>\u002Fen\u003C\u002Fcode>, plus whatever other multilingual fixes turn up before then. Then 1.0.0, the last Druxt on Nuxt 2, with Druxt 2.0.0, the Nuxt 4 rewrite under \u003Ccode>@druxt\u003C\u002Fcode>, on track for a first beta alongside it, building on the caching model 0.25.0 settled on.\u003C\u002Fp>",{"type":18,"layout":19,"title":157,"regions":158},"Experimenting with WebSockets",{"content":159},[160,162],{"type":23,"html":161},"\u003Cp>Something to play with while you wait. \u003Ca href=\"https:\u002F\u002Fumami.demo.druxtjs.org\u002Fplay\">Umami Go\u003C\u002Fa> is a multiplayer card game on the Umami demo, dealt from the magazine's recipes and rendered by Druxt. It is also where you can try the upcoming Druxt WebSockets module, still in development: edit a recipe in Drupal, and it changes in every hand holding it.\u003C\u002Fp>",{"type":30,"src":163,"alt":164,"caption":165,"width":34,"height":35},"\u002Fimages\u002Fwriting\u002Fdruxt-0250-caching-and-performance-buffs-20261023\u002Fumami-go.jpg","The Umami Go page: the title Umami Go in white italic serif on a dark brown lattice, the line 'A card game for two to five, dealt from this magazine's recipes', a Start a table button with a box to join by code, a fan of five recipe cards, and a 'Built on Druxt' badge with the Druxt icon.","Umami Go, a card game dealt from Drupal's Umami recipes, with a live table over the upcoming Druxt WebSockets module.",{"type":18,"layout":19,"regions":167},{"content":168},[169],{"type":170,"description":171,"url":172,"gitpod":173,"drupalUrl":174},"repository","\u003Cp>Druxt 1.0.0 and 2.x move as fast as sponsorship lets them.\u003C\u002Fp>","https:\u002F\u002Fgithub.com\u002Fdruxt\u002Fdruxt.js",false,"https:\u002F\u002Fwww.drupal.org\u002Fproject\u002Fdruxt","\u002Fwriting\u002Fdruxt-0250-caching-and-performance-buffs-20261023","7 min",{"loc":175},"articles-data\u002Fdruxt-0250-caching-and-performance-buffs-20261023","Qlp-D0XTK9mnBaj9NBzlg57BfrPCm-a0NxrmuDhdGg8",1792739510193]