[{"data":1,"prerenderedAt":959},["ShallowReactive",2],{"blog-nuxt-core-web-vitals-performance-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":13,"about":24,"sources":32,"cover":90,"og":91,"expertise":92,"locales":93,"lang":94,"title":97,"description":98,"coverAlt":99,"metaTitle":100,"takeaways":101,"faq":107,"toc":126,"blocks":160,"others":703},"nuxt-core-web-vitals-performance","2026-10-02",13,"web",[9,10,11,12],"Core Web Vitals","Nuxt","Performance","E-commerce",[14,15,16,17,18,19,20,21,22,23],"Core Web Vitals Nuxt","Nuxt performance optimization","improve LCP Nuxt","INP Nuxt hydration","fix CLS Vue","Nuxt lazy hydration","Nuxt Image LCP","Nuxt Scripts third-party scripts","Nuxt prerender vs SSR vs ISR","Core Web Vitals e-commerce",[25,27,29],{"name":9,"url":26},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCore_Web_Vitals",{"name":10,"url":28},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNuxt",{"name":30,"url":31},"Vue.js","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FVue.js",[33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81,84,87],{"title":34,"url":35},"web.dev: Web Vitals","https:\u002F\u002Fweb.dev\u002Farticles\u002Fvitals",{"title":37,"url":38},"web.dev: Largest Contentful Paint (LCP)","https:\u002F\u002Fweb.dev\u002Farticles\u002Flcp",{"title":40,"url":41},"web.dev: Optimize Largest Contentful Paint","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-lcp",{"title":43,"url":44},"web.dev: Interaction to Next Paint (INP)","https:\u002F\u002Fweb.dev\u002Farticles\u002Finp",{"title":46,"url":47},"web.dev: Optimize Cumulative Layout Shift","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-cls",{"title":49,"url":50},"web.dev: Optimize long tasks","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-long-tasks",{"title":52,"url":53},"Chrome for Developers: Chrome UX Report (CrUX)","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fcrux",{"title":55,"url":56},"Chrome for Developers: CrUX methodology","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fcrux\u002Fmethodology",{"title":58,"url":59},"GitHub: GoogleChrome\u002Fweb-vitals","https:\u002F\u002Fgithub.com\u002FGoogleChrome\u002Fweb-vitals",{"title":61,"url":62},"Nuxt docs: Rendering modes","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fconcepts\u002Frendering",{"title":64,"url":65},"Nuxt docs: Components (Lazy prefix, delayed hydration, client components)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fdirectory-structure\u002Fapp\u002Fcomponents",{"title":67,"url":68},"Nuxt docs: Experimental features (lazyHydration, payloadExtraction)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fgoing-further\u002Fexperimental-features",{"title":70,"url":71},"Nuxt docs: Data fetching (pick, transform, lazy)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Fdata-fetching",{"title":73,"url":74},"Nuxt blog: Nuxt 4.5","https:\u002F\u002Fnuxt.com\u002Fblog\u002Fv4-5",{"title":76,"url":77},"GitHub: Nuxt releases","https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases",{"title":79,"url":80},"Nuxt Image: NuxtImg","https:\u002F\u002Fimage.nuxt.com\u002Fusage\u002Fnuxt-img",{"title":82,"url":83},"Nuxt Fonts module","https:\u002F\u002Fnuxt.com\u002Fmodules\u002Ffonts",{"title":85,"url":86},"Nuxt Scripts: Getting started","https:\u002F\u002Fscripts.nuxt.com\u002Fdocs\u002Fgetting-started",{"title":88,"url":89},"Nuxt Scripts: Script triggers","https:\u002F\u002Fscripts.nuxt.com\u002Fdocs\u002Fguides\u002Fscript-triggers","\u002Fimages\u002Fblog\u002Fnuxt-core-web-vitals-performance\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-core-web-vitals-performance\u002Fog.jpg","vue-nuxt-developer",[94,95,96],"en","de","hu","Core Web Vitals for Nuxt sites and shops: fixing LCP, INP and CLS","How to fix LCP, INP and CLS in Nuxt 4 sites and shops: images, hydration, fonts, third-party scripts, prerender vs SSR vs ISR, and measuring real users.","Diagram: a Nuxt page load from server HTML through hero image, hydration and interaction, with the LCP, INP and CLS thresholds marked on the stages.","Core Web Vitals for Nuxt: LCP, INP, CLS · Balázs Csorba",[102,103,104,105,106],"Core Web Vitals are judged on field data at the 75th percentile: LCP at 2.5 seconds or less, INP at 200 milliseconds or less, CLS at 0.1 or less. Lighthouse cannot measure INP, so a green lab score proves little.","Most Nuxt LCP problems are discovery problems: the hero image is requested late, lazy-loaded or oversized. Fix the order of requests before you shave kilobytes.","Hydration is the main INP lever in a server-rendered Vue app. Ship less JavaScript, hydrate below-the-fold components lazily and break up long tasks.","CLS is almost always a missing reservation: image dimensions, font fallback metrics, late banners and consent bars, embeds without a reserved box.","Choose the rendering mode per route: prerender what rarely changes, cache with swr or isr where the platform supports it, and keep full SSR for what truly is per-request.",[108,111,114,117,120,123],{"q":109,"a":110},"What are good Core Web Vitals scores?","Google recommends LCP within 2.5 seconds, INP of 200 milliseconds or less and CLS of 0.1 or less. A page passes when it meets all three at the 75th percentile of real page loads, evaluated per device type, so slow phones and slow networks count as much as your laptop.",{"q":112,"a":113},"How do I improve LCP in a Nuxt app?","Find the LCP element, make sure it is in the server-rendered HTML, preload it with a high fetch priority and never lazy-load it. With Nuxt Image, use the preload prop with fetchPriority high, serve a modern format at the right size, and keep server response time low with prerendering or caching.",{"q":115,"a":116},"Why is INP bad even though Lighthouse is green?","Lighthouse loads a page in a simulated environment without a user, so it cannot measure INP at all; Total Blocking Time is only a proxy. INP measures every click, tap and key press in real sessions, including slow devices, heavy third-party scripts and work triggered after hydration.",{"q":118,"a":119},"Does lazy hydration help Core Web Vitals in Nuxt?","Yes, mainly INP and sometimes LCP. Nuxt supports hydration strategies such as hydrate-on-visible, hydrate-on-idle and hydrate-on-interaction on Lazy components, so below-the-fold widgets do not compete with the first interactions. Note that any prop change on such a component triggers hydration immediately.",{"q":121,"a":122},"Is SSR, prerendering or ISR best for Nuxt performance?","There is no single winner. Prerendering gives the fastest and most stable TTFB for content that changes rarely, swr or isr add cached regeneration for catalogue-style pages, and full SSR fits personalised or per-request pages. Nuxt route rules let you mix all of them in one app.",{"q":124,"a":125},"How do I measure Core Web Vitals for my real users?","Combine two sources. CrUX, through PageSpeed Insights or its API, shows what Chrome users experienced and is what Google sees. For your own segments and for debugging, add the web-vitals library, use its attribution build and send the metrics to your own endpoint with sendBeacon.",[127,130,133,136,139,142,145,148,151,154,157],{"id":128,"title":129},"measure-first","Measure first: field data beats lab data",{"id":131,"title":132},"where-metrics-come-from","Where each metric comes from in a Nuxt page load",{"id":134,"title":135},"lcp","LCP: make the biggest element discoverable and light",{"id":137,"title":138},"inp","INP: hydration, long tasks and what you ship",{"id":140,"title":141},"cls","CLS: reserve space for everything that arrives late",{"id":143,"title":144},"third-party-scripts","Third-party scripts: the tax you do not control",{"id":146,"title":147},"rendering-modes","Prerender, SSR or ISR: choose per route",{"id":149,"title":150},"fix-by-metric","Fix-by-metric table",{"id":152,"title":153},"shops","Shops are different: what I check on B2B stores",{"id":155,"title":156},"rollout-checklist","A rollout checklist",{"id":158,"title":159},"sources","Sources",[161,165,179,182,202,205,245,257,258,261,270,273,274,277,283,312,320,321,324,339,342,374,381,382,385,417,420,421,424,430,433,434,437,503,511,512,515,605,606,613,621,622,643,644],{"type":162,"content":163},"paragraph",[164],"A Nuxt site can score 100 in Lighthouse and still feel sluggish to the people who pay your bills. The reason is simple: Core Web Vitals are a field metric. They describe what real users on real devices experienced, at the 75th percentile, and that includes the mid-range Android phone on a train, not just your developer laptop.",{"type":162,"content":166},[167,168,172,173,178],"This article is the checklist I use for Nuxt sites and shops (Nuxt 4, currently in its 4.5 line). It is organised by metric: where LCP, INP and CLS come from in a server-rendered Vue app, which Nuxt features address each cause, and where the platform does not help you. It ends with a fix-by-metric table and a short rollout checklist. For context, I refactored a TYPO3 B2B shop for ",{"tag":169,"children":170},"strong",[171],"65% faster page loads (4.6 s down to 1.6 s)","; see ",{"tag":174,"to":175,"children":176},"link","\u002Freferences",[177],"references",". The principles below are the same, whichever stack sits behind the page.",{"type":180,"level":181,"id":128,"text":129},"heading",2,{"type":162,"content":183},[184,185,188,189,192,193,196,197,201],"The three Core Web Vitals and their targets are fixed: ",{"tag":169,"children":186},[187],"LCP"," (loading) within 2.5 seconds, ",{"tag":169,"children":190},[191],"INP"," (responsiveness) at 200 milliseconds or less, ",{"tag":169,"children":194},[195],"CLS"," (visual stability) at 0.1 or less. A page passes when it meets all three at the 75th percentile, segmented by device type. Google is explicit that ",{"tag":198,"href":35,"children":199},"a",[200],"lab measurement is not a substitute for field measurement",", and tools like Lighthouse, which load a page without a user, cannot measure INP at all. Total Blocking Time is only a proxy.",{"type":162,"content":203},[204],"That is why I never accept a performance ticket that reads \"Lighthouse says 98\". I want two field sources and one lab tool, each used for what it is good at:",{"type":206,"head":207,"rows":214},"table",[208,210,212],[209],"Source",[211],"What it tells you",[213],"Blind spot",[215,225,235],[216,221,223],[217,220],{"tag":169,"children":218},[219],"CrUX"," (via PageSpeed Insights, the CrUX API, BigQuery)",[222],"What Chrome users experienced on your origin or URL; the dataset Google looks at. Good for \"are we passing?\"",[224],"Only eligible, popular-enough pages; Chrome users who opted in; no iOS Chrome or WebView; no detail on causes",[226,231,233],[227,230],{"tag":169,"children":228},[229],"RUM"," (the web-vitals library, sent to your own endpoint)",[232],"All three metrics per page template, device, country or A\u002FB variant; the attribution build adds the element or interaction responsible",[234],"You build and maintain it; consent and privacy rules apply",[236,241,243],[237,240],{"tag":169,"children":238},[239],"Lighthouse \u002F DevTools"," (lab)",[242],"Reproducible runs for debugging LCP and CLS, request waterfalls, bundle analysis",[244],"No real users, no INP; a fast lab machine hides most problems",{"type":162,"content":246},[247,248,251,252,256],"The ",{"tag":198,"href":59,"children":249},[250],"web-vitals"," library gives you onLCP, onINP and onCLS; its attribution build, about 1.5 KB larger, adds diagnostic data such as which element was the LCP candidate. Send the values with navigator.sendBeacon to an endpoint you control, tag them with the route template (product page, category, checkout), and you can finally answer \"which page type is failing?\" instead of arguing about averages. If you also care about how agents and crawlers see these pages, the ",{"tag":174,"to":253,"children":254},"\u002Fblog\u002Fgenerative-engine-optimization-audit",[255],"GEO audit"," covers the other side of the same pages.",{"type":180,"level":181,"id":131,"text":132},{"type":162,"content":259},[260],"Nuxt renders universally by default: the server sends complete HTML, then the browser downloads the JavaScript and hydrates the page to make it interactive. That two-phase model explains most of the numbers. LCP is decided in the first half (server response, discovering and loading the biggest element), INP in the second (JavaScript execution and hydration compete with the user's first taps), and CLS runs through the whole life of the page.",{"type":262,"attrs":263,"inner":267,"caption":268},"diagram",{"viewBox":264,"role":265,"aria-labelledby":266},"0 0 770 270","img","d1-cwv-t d1-cwv-d","\u003Ctitle id=\"d1-cwv-t\">A Nuxt page load and its Core Web Vitals\u003C\u002Ftitle>\u003Cdesc id=\"d1-cwv-d\">Five stages from server HTML, hero image, first paint, hydration to interaction. LCP spans the first three stages, INP the last two, and CLS the whole page life.\u003C\u002Fdesc>\u003Ctext x=\"20\" y=\"28\" class=\"d-title\">A Nuxt page load and its Core Web Vitals\u003C\u002Ftext>\u003Ctext x=\"750\" y=\"28\" text-anchor=\"end\" class=\"d-label\">Universal rendering\u003C\u002Ftext>\u003Crect x=\"20\" y=\"52\" width=\"130\" height=\"64\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"85\" y=\"80\" text-anchor=\"middle\" class=\"d-text\">Server\u003C\u002Ftext>\u003Ctext x=\"85\" y=\"101\" text-anchor=\"middle\" class=\"d-small\">HTML, TTFB\u003C\u002Ftext>\u003Cpath d=\"M150 84 H162\" class=\"d-line\" \u002F>\u003Cpath d=\"M170 84 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"170\" y=\"52\" width=\"130\" height=\"64\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"235\" y=\"80\" text-anchor=\"middle\" class=\"d-text\">Hero image\u003C\u002Ftext>\u003Ctext x=\"235\" y=\"101\" text-anchor=\"middle\" class=\"d-small\">found + loaded\u003C\u002Ftext>\u003Cpath d=\"M300 84 H312\" class=\"d-line\" \u002F>\u003Cpath d=\"M320 84 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"320\" y=\"52\" width=\"130\" height=\"64\" rx=\"10\" class=\"d-mint\" \u002F>\u003Ctext x=\"385\" y=\"80\" text-anchor=\"middle\" class=\"d-text\">First paint\u003C\u002Ftext>\u003Ctext x=\"385\" y=\"101\" text-anchor=\"middle\" class=\"d-small\">LCP element\u003C\u002Ftext>\u003Cpath d=\"M450 84 H462\" class=\"d-line\" \u002F>\u003Cpath d=\"M470 84 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"470\" y=\"52\" width=\"130\" height=\"64\" rx=\"10\" class=\"d-accent\" \u002F>\u003Ctext x=\"535\" y=\"80\" text-anchor=\"middle\" class=\"d-text\">Hydration\u003C\u002Ftext>\u003Ctext x=\"535\" y=\"101\" text-anchor=\"middle\" class=\"d-small\">JS runs\u003C\u002Ftext>\u003Cpath d=\"M600 84 H612\" class=\"d-line\" \u002F>\u003Cpath d=\"M620 84 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"620\" y=\"52\" width=\"130\" height=\"64\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"685\" y=\"80\" text-anchor=\"middle\" class=\"d-text\">Interaction\u003C\u002Ftext>\u003Ctext x=\"685\" y=\"101\" text-anchor=\"middle\" class=\"d-small\">tap, click, key\u003C\u002Ftext>\u003Cpath d=\"M20 150 H450\" class=\"d-line-accent\" \u002F>\u003Cpath d=\"M20 144 v12 M450 144 v12\" class=\"d-line-accent\" \u002F>\u003Ctext x=\"235\" y=\"172\" text-anchor=\"middle\" class=\"d-text\">LCP: 2.5 s or less\u003C\u002Ftext>\u003Cpath d=\"M470 150 H750\" class=\"d-line-accent\" \u002F>\u003Cpath d=\"M470 144 v12 M750 144 v12\" class=\"d-line-accent\" \u002F>\u003Ctext x=\"610\" y=\"172\" text-anchor=\"middle\" class=\"d-text\">INP: 200 ms or less\u003C\u002Ftext>\u003Crect x=\"20\" y=\"206\" width=\"730\" height=\"46\" rx=\"10\" class=\"d-box d-dash\" \u002F>\u003Ctext x=\"385\" y=\"234\" text-anchor=\"middle\" class=\"d-small\">CLS: 0.1 or less, the whole page life, reserve space\u003C\u002Ftext>",[269],"LCP is mostly decided before hydration, INP after it, and CLS throughout. Fix them in that order of the timeline.",{"type":162,"content":271},[272],"Web.dev splits LCP into four subparts: time to first byte, resource load delay, resource load duration and element render delay. On a well-optimised page the guideline is roughly 40% TTFB, under 10% load delay, 40% load duration and under 10% render delay. The two delays should approach zero; in my audits they are where the easy wins hide, because nobody owns them.",{"type":180,"level":181,"id":134,"text":135},{"type":162,"content":275},[276],"The LCP element is usually an image (an img, a video poster or a CSS background image) or a large text block. First find out which one it is on each template, with DevTools or the attribution build; on shop category pages it is often a banner, on product pages the main product photo. Then work through the subparts.",{"type":162,"content":278},[279,282],{"tag":169,"children":280},[281],"Discovery."," The LCP resource should be discoverable in the HTML source. A hero that is injected by client-side JavaScript, a CSS background image, or a carousel that renders only after hydration all push the request behind your JavaScript bundle. With Nuxt Image, the NuxtImg component has a preload prop that places a link tag in the head and accepts a fetchPriority of high. Never put loading=\"lazy\" on the LCP image: web.dev names it as a direct cause of resource load delay.",{"type":284,"ordered":285,"items":286},"list",false,[287,292,297,302,307],[288,291],{"tag":169,"children":289},[290],"Right format and size."," NuxtImg supports format, quality and sizes, so one source file becomes responsive, modern variants (webp or avif). I set explicit sizes for every hero and product image; an image served at 2,000 pixels to a 400-pixel slot is the most common single waste I see.",[293,296],{"tag":169,"children":294},[295],"Explicit dimensions."," Give width and height (or an aspect ratio) so the browser reserves the space. This helps LCP discovery and, as shown below, is also the first CLS fix.",[298,301],{"tag":169,"children":299},[300],"Server response time."," TTFB is the largest share of the LCP budget. Prerender or cache what you can (see the rendering section), and keep slow data calls out of the critical path with the lazy option or useLazyFetch for non-critical data.",[303,306],{"tag":169,"children":304},[305],"Fonts."," If the LCP element is text, a late web font delays it. Preload the critical font and use a fallback with matched metrics (next section on CLS).",[308,311],{"tag":169,"children":309},[310],"No render-blocking surprises."," A consent banner, an A\u002FB-testing snippet that hides the page, or a synchronous third-party script in the head can delay render even when everything else is perfect.",{"type":162,"content":313},[314,315,319],"Nuxt 4.5 also added experimental SSR streaming, which flushes the HTML shell instead of buffering the whole page and is aimed squarely at TTFB. It is experimental, it is disabled for bots, and anything that changes the response after rendering started (headers, redirects) cannot reach the client, so I would trial it on one route with RUM before adopting it. The same streaming mindset, for AI features, is in ",{"tag":174,"to":316,"children":317},"\u002Fblog\u002Fnuxt-llm-features-ai-sdk-streaming",[318],"LLM features in Nuxt with the AI SDK",".",{"type":180,"level":181,"id":137,"text":138},{"type":162,"content":322},[323],"INP observes the latency of every click, tap and key press during a visit, from input delay through event handlers to the next frame painted. Good is 200 ms or less; above 500 ms is poor. It replaced First Input Delay because FID only looked at the first interaction. Scrolling and hovering are not counted.",{"type":162,"content":325},[326,327,330,331,334,335,338],"In a server-rendered Vue app the pattern is consistent: the page looks ready, the user taps, and the main thread is still busy hydrating or running a third-party tag. That is ",{"tag":169,"children":328},[329],"input delay"," from long tasks (anything over 50 ms blocks the main thread). Add slow event handlers (",{"tag":169,"children":332},[333],"processing duration",") and expensive re-renders or layout work (",{"tag":169,"children":336},[337],"presentation delay","), and you have the three places INP is lost.",{"type":162,"content":340},[341],"What I do about it, in order of payoff:",{"type":284,"ordered":285,"items":343},[344,349,354,359,364,369],[345,348],{"tag":169,"children":346},[347],"Ship less JavaScript."," Check the bundle with a visualizer before touching any code. Every heavy library imported on a page needs a justification; a date library or a rich-text editor on a catalogue page rarely has one.",[350,353],{"tag":169,"children":351},[352],"Hydrate lazily."," Nuxt supports delayed hydration on Lazy components: hydrate-on-visible, hydrate-on-idle, hydrate-on-interaction, hydrate-on-media-query, hydrate-after, hydrate-when and hydrate-never. Reviews, recommendation carousels, footers and chat widgets do not need to be interactive at first paint. The caveat: any prop change on a lazily hydrated component triggers hydration immediately, and it works with single-file components and template props, not v-bind spreads.",[355,358],{"tag":169,"children":356},[357],"Render once, ship less state."," Nuxt serialises fetched data into the payload. Use pick or transform on useFetch and useAsyncData so only the fields the template needs are sent; the docs note they do not stop the data being fetched, but they keep it out of the payload. Payload extraction is on by default, and the stripNeverHydratedData experiment keeps data of hydrate-never components out too.",[360,363],{"tag":169,"children":361},[362],"Keep static things static."," Components that never change after render (marketing blocks, footers) are candidates for hydrate-never or a server-only approach, so Vue does not create reactive state for them.",[365,368],{"tag":169,"children":366},[367],"Break up long tasks."," Where you must do heavy work (filtering a large product list, parsing), yield to the main thread. web.dev recommends scheduler.yield() with a setTimeout fallback and yielding about every 50 ms of work rather than after every item.",[370,373],{"tag":169,"children":371},[372],"Tame the handlers."," Debounce expensive input handlers, avoid synchronous layout reads in loops, and show immediate feedback (a pressed state, a spinner) before slow work, because INP ends at the next painted frame.",{"type":375,"variant":376,"title":377,"body":378},"callout","tip","Test INP the way users trigger it",[379],[380],"Open DevTools with CPU throttling, reload, and click the main navigation, the search field and the add-to-cart button within the first seconds. If any of them lags, that is the real INP story, not the lab score. Then confirm with RUM attribution which interaction is the worst in the field.",{"type":180,"level":181,"id":140,"text":141},{"type":162,"content":383},[384],"Layout shift is the most mechanical metric: something appeared or changed size and pushed content that was already visible. In Nuxt shops I find the same culprits again and again:",{"type":284,"ordered":285,"items":386},[387,392,397,402,407,412],[388,391],{"tag":169,"children":389},[390],"Images and media without dimensions."," Always set width and height; browsers derive the aspect ratio from them. For responsive images the CSS aspect-ratio property does the same job.",[393,396],{"tag":169,"children":394},[395],"Web fonts."," Both flash of unstyled text and flash of invisible text can shift layout. The Nuxt Fonts module applies automatic font metric optimisation (via fontaine and capsize) so the fallback font occupies the same space as the web font, supports local download of providers such as Google, and removes a whole class of font-related shifts almost for free. Preloading critical fonts and font-display: optional are the other web.dev recommendations.",[398,401],{"tag":169,"children":399},[400],"Late banners."," Cookie and consent bars, promotion strips and \"free shipping from\" notices that are injected after hydration push the whole page down. Render them on the server in reserved space or overlay them with a fixed position instead of inserting them in the flow.",[403,406],{"tag":169,"children":404},[405],"Embeds and ads."," Reserve a box with min-height or aspect-ratio before an iframe, map or video loads. The same goes for lazily hydrated components that change height when they become interactive.",[408,411],{"tag":169,"children":409},[410],"Client-only content."," A .client.vue component or ClientOnly block renders only after mount; without a placeholder of the right size it will shift the page. Use the fallback slot to render a skeleton with the final dimensions.",[413,416],{"tag":169,"children":414},[415],"Animations."," Animate with transform instead of top, left or height; composited animations do not contribute to CLS.",{"type":162,"content":418},[419],"One more cheap win: make pages eligible for the back\u002Fforward cache. Restored pages are instant and incur no new layout shifts, which matters in shops where users bounce between listing and detail pages.",{"type":180,"level":181,"id":143,"text":144},{"type":162,"content":422},[423],"Analytics, tag managers, chat widgets, review badges and A\u002FB testing tools run on the same main thread as your Vue app. They are the usual reason why a lean Nuxt bundle still produces a poor INP and why LCP regresses after marketing \"just adds one tag\". Treat each script as a budgeted dependency with an owner.",{"type":162,"content":425},[426,429],{"tag":198,"href":86,"children":427},[428],"Nuxt Scripts"," is the official way to control this. Its useScript composable loads third-party scripts with SSR support, delayed loading and typed APIs. The default trigger, onNuxtReady, waits for hydration and then schedules the load in an idle period. Other options are manual loading, useScriptTriggerIdleTimeout, useScriptTriggerInteraction (first scroll, click or key), useScriptTriggerElement (when an element becomes visible) and consent-based triggers. There are registry integrations for common services such as Google Analytics, Google Maps, YouTube and Stripe, and facade components that show a lightweight placeholder until the real embed is needed.",{"type":162,"content":431},[432],"My rules: nothing third-party in the critical rendering path; chat and video behind interaction or visibility triggers; analytics after hydration and consent; every script reviewed against its INP cost in RUM before and after release. Reserve the layout space for anything visual (see CLS). For a B2B shop with logged-in buyers, I would also question whether a heatmap tool needs to run in the checkout at all.",{"type":180,"level":181,"id":146,"text":147},{"type":162,"content":435},[436],"The rendering mode sets your TTFB floor and therefore your LCP ceiling. Nuxt's hybrid rendering lets you decide per route with route rules instead of one global setting:",{"type":206,"head":438,"rows":447},[439,441,443,445],[440],"Mode (route rule)",[442],"What it does",[444],"Use it for",[446],"Watch out",[448,459,470,481,492],[449,453,455,457],[450],{"tag":169,"children":451},[452],"prerender: true",[454],"Generates static HTML at build time",[456],"Landing pages, docs, blog posts, legal pages",[458],"Content is only as fresh as the last build",[460,464,466,468],[461],{"tag":169,"children":462},[463],"swr",[465],"Serves a cached response and regenerates it in the background",[467],"Category and listing pages that change a few times a day",[469],"The first visitor after expiry may get stale content; needs a server or platform cache",[471,475,477,479],[472],{"tag":169,"children":473},[474],"isr",[476],"CDN-cached pages with revalidation; supported on Netlify and Vercel via native integration",[478],"Large catalogues where a full rebuild is too slow",[480],"Platform-specific; check your hosting before designing around it",[482,486,488,490],[483],{"tag":169,"children":484},[485],"Universal SSR (default)",[487],"Renders HTML per request, then hydrates",[489],"Personalised or per-request pages: cart, account, price lists",[491],"TTFB depends on your backend and data calls",[493,497,499,501],[494],{"tag":169,"children":495},[496],"ssr: false",[498],"Client-only rendering for that route",[500],"Authenticated dashboards behind a login",[502],"Slower first load and weaker SEO visibility; keep it away from landing pages",{"type":162,"content":504},[505,506,510],"For a B2B shop, the split is usually clear: marketing, content and category pages are prerendered or cached, product pages use swr or isr with price and stock fetched client-side after hydration (or edge-side per customer), and cart, checkout and account stay on SSR. The trap is personalisation: one customer-specific price in the server-rendered HTML turns every cached page into a per-request page. Keep the shell cacheable and load the personal part separately, with a reserved box so it does not cause layout shift. The same shop pages are what ",{"tag":174,"to":507,"children":508},"\u002Fblog\u002Fagentic-commerce-protocols-ucp-acp-guide",[509],"agentic commerce protocols"," and agents will fetch, so a fast, cacheable shell pays off twice.",{"type":180,"level":181,"id":149,"text":150},{"type":162,"content":513},[514],"The condensed version, for pinning next to your RUM dashboard:",{"type":206,"head":516,"rows":525},[517,519,521,523],[518],"Metric",[520],"Typical cause in Vue\u002FNuxt",[522],"First fix",[524],"Nuxt tool",[526,536,546,555,565,575,585,595],[527,530,532,534],[528],{"tag":169,"children":529},[187],[531],"Hero image found late, lazy-loaded or oversized",[533],"Put it in server HTML, preload it, right size and format, never lazy-load it",[535],"NuxtImg: preload with fetchPriority high, sizes, format, quality",[537,540,542,544],[538],{"tag":169,"children":539},[187],[541],"High TTFB from SSR and slow data calls",[543],"Prerender or cache; move non-critical data off the critical path",[545],"routeRules (prerender, swr, isr), useLazyFetch, experimental SSR streaming",[547,550,552,554],[548],{"tag":169,"children":549},[187],[551],"Text LCP waits for a web font",[553],"Preload critical font, metric-matched fallback",[82],[556,559,561,563],[557],{"tag":169,"children":558},[191],[560],"Main thread busy hydrating the whole page",[562],"Hydrate below-the-fold and non-interactive parts lazily or never",[564],"Lazy components with hydrate-on-visible, hydrate-on-idle, hydrate-on-interaction, hydrate-never",[566,569,571,573],[567],{"tag":169,"children":568},[191],[570],"Large bundles and payload",[572],"Remove or split heavy libraries; send only fields the template uses",[574],"Lazy prefix code-splitting, pick and transform, payload extraction",[576,579,581,583],[577],{"tag":169,"children":578},[191],[580],"Third-party tags and long tasks",[582],"Defer tags, yield in heavy loops, debounce handlers",[584],"Nuxt Scripts triggers, scheduler.yield() with fallback",[586,589,591,593],[587],{"tag":169,"children":588},[195],[590],"Images and embeds without reserved space",[592],"width and height or aspect-ratio on everything that loads later",[594],"NuxtImg with dimensions, CSS aspect-ratio",[596,599,601,603],[597],{"tag":169,"children":598},[195],[600],"Font swap, late banners, client-only blocks",[602],"Metric-matched fallbacks; reserve banner space; skeleton fallbacks",[604],"Nuxt Fonts, ClientOnly fallback slot",{"type":180,"level":181,"id":152,"text":153},{"type":162,"content":607},[608,609,612],"In a TYPO3 B2B shop refactoring I reduced page loads by 65%, from 4.6 s to 1.6 s (",{"tag":174,"to":175,"children":610},[611],"details in references","). The principles in this article are the ones I apply there too: measure first, get the critical path short, and treat every extra request and script as something that must earn its place.",{"type":162,"content":614},[615,616,620],"Shops add three specific traps. Product listings render hundreds of images and filters, so lazy-load everything except the first row and measure INP on filter interactions, not just on load. Prices, stock and customer-specific catalogues tempt you to disable caching for the whole page. And the shop's tag stack (analytics, remarketing, reviews, live chat) tends to grow every quarter. If you are building or migrating a Nuxt storefront, my ",{"tag":174,"to":617,"children":618},"\u002Fexpertise\u002Fvue-nuxt-developer",[619],"Vue and Nuxt expertise page"," lists what I take on.",{"type":180,"level":181,"id":155,"text":156},{"type":284,"ordered":623,"items":624},true,[625,627,629,631,633,635,637,639,641],[626],"Set up RUM with the web-vitals attribution build, tagged by route template, and keep the CrUX numbers for your origin next to it.",[628],"Pick the three worst templates by traffic and failing metric; ignore the rest until those pass.",[630],"Identify the LCP element on each template and make it discoverable in the HTML, preloaded, right-sized and never lazy-loaded.",[632],"Assign a rendering mode per route with route rules; keep personalised data out of cached HTML.",[634],"Add the Nuxt Fonts module and verify fallback metrics; add width, height or aspect-ratio to every image and embed.",[636],"Check the bundle and payload; apply pick or transform; split heavy components with the Lazy prefix.",[638],"Add hydration strategies to below-the-fold and non-interactive components; re-test the first taps under CPU throttling.",[640],"Move every third-party script to Nuxt Scripts with a trigger, an owner and a measured INP cost.",[642],"Add a performance budget to CI (bundle size, Lighthouse for LCP and CLS) and watch RUM over a few weeks of real traffic before declaring victory.",{"type":180,"level":181,"id":158,"text":159},{"type":284,"ordered":623,"items":645},[646,649,652,655,658,661,664,667,670,673,676,679,682,685,688,691,694,697,700],[647],{"tag":198,"href":35,"children":648},[34],[650],{"tag":198,"href":38,"children":651},[37],[653],{"tag":198,"href":41,"children":654},[40],[656],{"tag":198,"href":44,"children":657},[43],[659],{"tag":198,"href":47,"children":660},[46],[662],{"tag":198,"href":50,"children":663},[49],[665],{"tag":198,"href":53,"children":666},[52],[668],{"tag":198,"href":56,"children":669},[55],[671],{"tag":198,"href":59,"children":672},[58],[674],{"tag":198,"href":62,"children":675},[61],[677],{"tag":198,"href":65,"children":678},[64],[680],{"tag":198,"href":68,"children":681},[67],[683],{"tag":198,"href":71,"children":684},[70],[686],{"tag":198,"href":74,"children":687},[73],[689],{"tag":198,"href":77,"children":690},[76],[692],{"tag":198,"href":80,"children":693},[79],[695],{"tag":198,"href":83,"children":696},[82],[698],{"tag":198,"href":86,"children":699},[85],[701],{"tag":198,"href":89,"children":702},[88],[704,789,845,912],{"slug":705,"published":5,"minutes":6,"category":7,"tags":706,"keywords":711,"about":722,"sources":730,"cover":782,"og":783,"expertise":784,"locales":785,"lang":94,"title":786,"description":787,"coverAlt":788},"european-accessibility-act-b2b-shop",[707,708,709,710],"European Accessibility Act","BFSG","WCAG 2.2","B2B e-commerce",[712,713,714,715,716,717,718,719,720,721],"European Accessibility Act B2B shop","BFSG B2B Onlineshop","Barrierefreiheit Onlineshop","Spryker barrierefrei","barrierefrei Webshop","BaFG Österreich Webshop","EN 301 549 WCAG 2.2","BFSG Kleinstunternehmen Ausnahme","Pimcore TYPO3 Barrierefreiheit","EAA e-commerce checklist",[723,725,728],{"name":707,"url":724},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEuropean_Accessibility_Act",{"name":726,"url":727},"Web Content Accessibility Guidelines","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWeb_Content_Accessibility_Guidelines",{"name":12,"url":729},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FE-commerce",[731,734,737,740,743,746,749,752,755,758,761,764,767,770,773,776,779],{"title":732,"url":733},"BFSG section 1: purpose and scope (gesetze-im-internet.de)","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__1.html",{"title":735,"url":736},"BFSG section 2: definitions, consumer, microenterprise, e-commerce services","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__2.html",{"title":738,"url":739},"BFSG section 3: accessibility and the microenterprise exemption","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__3.html",{"title":741,"url":742},"BFSG section 14: duties of the service provider","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__14.html",{"title":744,"url":745},"BFSG section 32: rights of consumers and associations in the administrative procedure","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__32.html",{"title":747,"url":748},"BFSG section 37: fines","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__37.html",{"title":750,"url":751},"BFSG section 38: transitional provisions","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__38.html",{"title":753,"url":754},"Bundesfachstelle Barrierefreiheit: FAQ on the BFSG (B2B, microenterprises, EN 301 549)","https:\u002F\u002Fwww.bundesfachstelle-barrierefreiheit.de\u002FDE\u002FFachwissen\u002FProdukte-und-Dienstleistungen\u002FBarrierefreiheitsstaerkungsgesetz\u002FFAQ\u002Ffaq_node.html",{"title":756,"url":757},"AccessibleEU: The European accessibility standard EN 301 549 has been updated (7 September 2026)","https:\u002F\u002Faccessible-eu-centre.ec.europa.eu\u002Fcontent-corner\u002Fnews\u002Feuropean-accessibility-standard-en-301-549-has-been-updated-2026-09-07_en",{"title":759,"url":760},"W3C: Web Content Accessibility Guidelines (WCAG) 2.2","https:\u002F\u002Fwww.w3.org\u002FTR\u002FWCAG22\u002F",{"title":762,"url":763},"Deque: Automated testing identifies 57 percent of digital accessibility issues","https:\u002F\u002Fwww.deque.com\u002Fblog\u002Fautomated-testing-study-identifies-57-percent-of-digital-accessibility-issues\u002F",{"title":765,"url":766},"sitebrunch: What is the market surveillance authority for accessibility (MLBF)?","https:\u002F\u002Fwww.sitebrunch.com\u002Fnews\u002Fmlbf-marktueberwachungsstelle-barrierefreiheit",{"title":768,"url":769},"Marcus Herrmann: June 2026, how the MLBF intends to check","https:\u002F\u002Fmarcus-herrmann.com\u002Fblog\u002Fmlbf-verraet-wie-sie-pruefen-will",{"title":771,"url":772},"axes4: BFSG in practice, what has happened since the deadline (2026)","https:\u002F\u002Fwww.axes4.com\u002Fde\u002Fblog\u002Fpost\u002F2026\u002Fbfsg-in-der-praxis-was-sich-seit-dem-stichtag-getan-hat",{"title":774,"url":775},"ODC Legal: BFSG for websites, what companies must check in 2026","https:\u002F\u002Fwww.odclegal.de\u002Fblog\u002Fbfsg-website-pflicht",{"title":777,"url":778},"WKO: Information on the Austrian Barrierefreiheitsgesetz","https:\u002F\u002Fwww.wko.at\u002Fce-kennzeichnung-normen\u002Finformationen-zum-barrierefreiheitsgesetz",{"title":780,"url":781},"Web Crossing: One year of the Barrierefreiheitsgesetz, where online shops must improve","https:\u002F\u002Fwww.web-crossing.com\u002Fnews\u002Fdetail\u002Fein-jahr-barrierefreiheitsgesetz-wo-onlineshops-und-websites-jetzt-nachbessern-muessen\u002F","\u002Fimages\u002Fblog\u002Feuropean-accessibility-act-b2b-shop\u002Fcover.webp","\u002Fimages\u002Fblog\u002Feuropean-accessibility-act-b2b-shop\u002Fog.jpg","b2b-ecommerce-developer",[94,95,96],"European Accessibility Act for B2B shops: what Spryker, Pimcore and TYPO3 teams must fix","Does the European Accessibility Act apply to a B2B shop? Scope under BFSG and BaFG, the microenterprise exemption, 2026 enforcement, WCAG 2.2 and a fix plan.","Diagram: a B2B shop fans out to the consumer-scope question under BFSG and BaFG, WCAG 2.2 AA conformity, the accessibility statement and market surveillance.",{"slug":790,"published":5,"minutes":791,"category":7,"tags":792,"keywords":795,"about":806,"sources":812,"cover":839,"og":840,"expertise":784,"locales":841,"lang":94,"title":842,"description":843,"coverAlt":844},"headless-product-configurator-b2b",12,[793,710,10,794],"Product configurator","Headless commerce",[796,797,798,799,800,801,802,803,804,805],"B2B product configurator","Produktkonfigurator","Produktkonfigurator TYPO3","B2B Konfigurator","headless product configurator","CPQ configure price quote","rule engine vs constraint solver","Nuxt Spryker Glue API","product configurator 100000 variants","accessible product configurator",[807,809,811],{"name":793,"url":808},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct_configurator",{"name":794,"url":810},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHeadless_commerce",{"name":10,"url":28},[813,815,818,821,824,827,830,833,836],{"title":814,"url":808},"Wikipedia: Knowledge-based configuration (product configurator)",{"title":816,"url":817},"Google OR-Tools: The CP-SAT solver","https:\u002F\u002Fdevelopers.google.com\u002Foptimization\u002Fcp\u002Fcp_solver",{"title":819,"url":820},"Spryker documentation: Glue API, Product Configuration feature integration","https:\u002F\u002Fdocs.spryker.com\u002Fdocs\u002Fscos\u002Fdev\u002Ffeature-integration-guides\u002F202108.0\u002Fglue-api\u002Fglue-api-product-configuration-feature-integration.html",{"title":822,"url":823},"OWASP: Input Validation Cheat Sheet","https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FInput_Validation_Cheat_Sheet.html",{"title":825,"url":826},"web.dev: Virtualize large lists","https:\u002F\u002Fweb.dev\u002Farticles\u002Fvirtualize-long-lists-react-window",{"title":828,"url":829},"W3C: Understanding SC 3.3.1 Error Identification (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Ferror-identification.html",{"title":831,"url":832},"W3C: Understanding SC 4.1.3 Status Messages (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Fstatus-messages.html",{"title":834,"url":835},"W3C: Understanding SC 2.5.8 Target Size (Minimum) (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Ftarget-size-minimum.html",{"title":837,"url":838},"W3C WAI-ARIA Authoring Practices: Combobox pattern","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FARIA\u002Fapg\u002Fpatterns\u002Fcombobox\u002F","\u002Fimages\u002Fblog\u002Fheadless-product-configurator-b2b\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fheadless-product-configurator-b2b\u002Fog.jpg",[94,95,96],"Headless B2B product configurator: rules, pricing and Nuxt on a commerce API","How to build a B2B product configurator headless: rule engine or solver, where rules live, server-side pricing, Nuxt on a commerce API, TYPO3 and 100k+ variants.","Diagram: a Nuxt front end talks to a configuration service that reads rules from the PIM and prices from the ERP, then hands a validated configuration to the commerce API.",{"slug":846,"published":5,"minutes":791,"category":7,"tags":847,"keywords":852,"about":863,"sources":872,"cover":906,"og":907,"expertise":784,"locales":908,"lang":94,"title":909,"description":910,"coverAlt":911},"pimcore-erp-delta-sync",[848,849,850,851],"Pimcore","SAP integration","PIM ERP sync","Symfony Messenger",[853,854,855,856,857,858,859,860,861,862],"Pimcore ERP delta sync","Pimcore developer","Pimcore SAP integration","PIM ERP Schnittstelle","Pimcore SAP Schnittstelle","PIM ERP sync best practices","Pimcore Symfony Messenger","delta sync vs full sync","idempotent import Pimcore","Pimcore Infor integration",[864,866,869],{"name":848,"url":865},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPimcore",{"name":867,"url":868},"SAP ERP","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSAP_ERP",{"name":870,"url":871},"Product information management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct_information_management",[873,876,879,882,885,888,891,894,897,900,903],{"title":874,"url":875},"Pimcore docs: Symfony Messenger","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FGetting_Started\u002FInstallation\u002FAdvanced_Installation_Topics\u002FSymfony_Messenger\u002F",{"title":877,"url":878},"Pimcore docs: Platform Versions","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FPimcore_Platform\u002FPlatform_Versions\u002F",{"title":880,"url":881},"Pimcore docs: Pimcore Editions","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FPimcore_Platform\u002FPimcore_Editions\u002F",{"title":883,"url":884},"Pimcore docs: Data Importer","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FData_Importer\u002F",{"title":886,"url":887},"Pimcore on GitHub: Release 2026.3.0","https:\u002F\u002Fgithub.com\u002Fpimcore\u002Fpimcore\u002Freleases\u002Ftag\u002Fv2026.3.0",{"title":889,"url":890},"Symfony docs: Messenger, Sync & Queued Message Handling","https:\u002F\u002Fsymfony.com\u002Fdoc\u002Fcurrent\u002Fmessenger.html",{"title":892,"url":893},"SAP Library: Change Pointer (Master Data Distribution)","https:\u002F\u002Fhelp.sap.com\u002Fdoc\u002Fsaphelp_nw73ehp1\u002F7.31.19\u002Fen-us\u002F4a\u002Fbb1e253536478be10000000a421937\u002Fcontent.htm?no_cache=true",{"title":895,"url":896},"SAP Library: Change Pointer (IDoc Interface\u002FALE)","https:\u002F\u002Fhelp.sap.com\u002Fdoc\u002Fsaphelp_em700_ehp01\u002F7.0.1\u002Fen-US\u002F12\u002F83e03c19758e71e10000000a114084\u002Fcontent.htm?no_cache=true",{"title":898,"url":899},"Infor docs: Infor ION and M3 BODs","https:\u002F\u002Fdocs.infor.com\u002Fm3cs\u002F10.2.3\u002Fen-us\u002Fm3csbom\u002Ffabsog\u002Fgoi1495809108914.html",{"title":901,"url":902},"Infor Developer Portal: Integration with ION","https:\u002F\u002Fdeveloper.infor.com\u002Ftutorials\u002Fintegration-with-ion",{"title":904,"url":905},"Debezium: open source distributed platform for change data capture","https:\u002F\u002Fdebezium.io\u002F","\u002Fimages\u002Fblog\u002Fpimcore-erp-delta-sync\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fpimcore-erp-delta-sync\u002Fog.jpg",[94,95,96],"Pimcore ERP delta sync: syncing product data between SAP or Infor, PIM and shop","How to sync product data between SAP or Infor, Pimcore and a shop: delta vs full sync, change detection, idempotent imports, Messenger queues, ownership and replays.","Diagram: product data flows from an SAP or Infor ERP through a delta extract and a message queue into Pimcore, where quality gates run, and on to the shop.",{"slug":913,"published":914,"minutes":915,"category":7,"tags":916,"keywords":922,"about":931,"sources":940,"cover":953,"og":954,"expertise":92,"locales":955,"lang":94,"title":956,"description":957,"coverAlt":958},"home-assistant-ev-charging-energy-manager","2026-09-29",9,[917,918,919,920,921],"Home Assistant","EPEX Spot","Energy prices","Tesla","Energy management",[923,924,925,926,927,928,929,930],"EPEX Austria prices","spot tariff Austria EV charging","Home Assistant EV charging","cheapest hours charging","day-ahead price Austria","negative electricity prices Austria","Tesla charging spot prices","load management 3 phase",[932,935,937],{"name":933,"url":934},"EPEX SPOT","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEPEX_SPOT",{"name":917,"url":936},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHome_Assistant",{"name":938,"url":939},"Load management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLoad_management",[941,944,947,950],{"title":942,"url":943},"aWATTar Austria: market data API","https:\u002F\u002Fwww.awattar.at\u002Fservices\u002Fapi",{"title":945,"url":946},"EPEX SPOT: market results","https:\u002F\u002Fwww.epexspot.com\u002Fen\u002Fmarket-results",{"title":948,"url":949},"Home Assistant developer docs: apps","https:\u002F\u002Fdevelopers.home-assistant.io\u002Fdocs\u002Fapps\u002F",{"title":951,"url":952},"Home Assistant: Tesla Fleet integration","https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Ftesla_fleet\u002F","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fog.jpg",[94,95,96],"Charging on EPEX Austria prices: what my Home Assistant app saves","A year of hourly EPEX prices for Austria, replayed for a Tesla and a water boiler: cheapest-hour charging costs 6.5 instead of 18.9 ct\u002FkWh, close to €590 a year, without blowing a 20 A fuse.","Cover: three bars comparing 18.9, 13.1 and 6.5 ct\u002FkWh for charging at 18:00, overnight and in the cheapest hours.",1791009037153]