[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"contentNavigation":3,"topic-best-practices":4,"newsletter-stats":9,"articles-feed-\u002Ftopics\u002Fbest-practices-1-best-practices-":11,"$fQa3xUpnRDPNiFFu2QS4K-DAjYmsapeR9fNhPNBdpkOc":-1,"home-tags":118},[],{"articleCount":5,"color":6,"id":7,"name":8,"slug":8},4,"#10b981","019d6bd9-010e-772d-b2f0-9378a042a676","best-practices",{"confirmedCount":10},405,{"items":12,"page":116,"pageSize":117,"totalCount":5},[13,44,70,94],{"content":14,"createdAt":15,"id":16,"image":17,"isAffiliate":18,"isPublished":19,"publishedAt":20,"slug":21,"sourceId":22,"sourceName":23,"sourceType":24,"summary":25,"title":26,"updatedAt":27,"url":28,"urlHash":29,"tags":30},"As Vue applications grow, keeping the codebase clean becomes increasingly challenging. At first, everyone follows the same conventions. But over time, you start noticing things like: inconsistent component structure multiple ways of solving the same problem different Composition API patterns imports scattered everywhere code that's difficult to review and maintain This is where ESLint becomes much more than a tool for formatting—it becomes a way to enforce your project's architecture and design patterns. In this article, we'll explore: Why ESLint is more than a linter How it helps maintain a consistent architecture Useful Vue-specific rules Examples of enforcing project-wide patterns Best practices for scaling Vue applications Let's dive in. 🤔 ESLint Is More Than a Code Formatter Many developers think ESLint is only used to catch things like; unused variables, missing semicolons, incorrect formatting. While that's true, ESLint can do much more. With the right configuration, it can enforce: coding conventions project architecture Vue best practices Composition API patterns import organization component structure Instead of relying on code reviews to catch inconsistencies, ESLint prevents them before the code is even merged. 🟢 What Problem Does It Solve? Imagine a team of five developers. One component looks like this: &lt;script setup&gt; ... &lt;\u002Fscript&gt; &lt;template&gt; ... &lt;\u002Ftemplate&gt; &lt;style&gt; ... &lt;\u002Fstyle&gt; Another one: &lt;template&gt; ... &lt;\u002Ftemplate&gt; &lt;script setup&gt; ... &lt;\u002Fscript&gt; Someone uses: watch() Someone else always prefers: watchEffect() Some developers use relative imports: ..\u002F..\u002F..\u002Fcomponents\u002FButton.vue Others use aliases: ~\u002Fcomponents\u002FButton.vue None of these are necessarily wrong... but together they create inconsistency across the project. ESLint helps eliminate these differences by enforcing one agreed-upon way of writing code. 🟢 Enforcing Vue Block Order One of the simplest examples is keeping Vue Single File Components organized. Using the vue\u002Fblock-order rule, you can define the exact order of blocks. Example: &lt;template&gt; ... &lt;\u002Ftemplate&gt; &lt;script setup lang=\"ts\"&gt; ... &lt;\u002Fscript&gt; &lt;style scoped&gt; ... &lt;\u002Fstyle&gt; 🟢 Enforcing Composition API Patterns One of the biggest advantages of ESLint is enforcing how developers use Vue APIs. For example, you might decide that your project should always use: &lt;script setup&gt; Composition API defineProps defineEmits And avoid Options API or inconsistent component definitions. This ensures new components follow the same architecture from day one. 🟢 Restricting Specific APIs Sometimes you want to discourage certain patterns altogether. For example, you may decide: ❌ Avoid watch() unless absolutely necessary. Prefer: computed() or watchEffect() ESLint can restrict the use of specific APIs and encourage better alternatives. This keeps reactive logic predictable and easier to maintain. 🟢 Enforcing Import Conventions Large Vue projects often suffer from inconsistent imports. Example: import Button from '..\u002F..\u002F..\u002Fcomponents\u002FButton.vue' vs. import Button from '~\u002Fcomponents\u002FButton.vue' ESLint can enforce: path aliases import ordering grouped imports no duplicate imports The result is cleaner and more consistent code. 🟢 Preventing Architectural Violations ESLint can also help enforce architectural boundaries. For example: Components shouldn't access API clients directly. Feature modules shouldn't import from other features. UI components shouldn't depend on business logic. Using rules such as no-restricted-imports, you can prevent these patterns entirely. Example: 'no-restricted-imports': [ 'error', { patterns: [ '@\u002Fapi\u002F*' ] } ] Now components can't accidentally bypass your intended architecture. 🟢 Creating Custom Rules for Your Team One of ESLint's greatest strengths is extensibility. Beyond built-in rules, you can create custom rules—or use plugins—to enforce project-specific conventions. For example: composables must start with use stores must live in a dedicated folder feature modules cannot import each other utility functions must remain pure internal design system components must be used instead of native HTML elements As your project grows, these automated checks become far more reliable than relying solely on code reviews. 🟢 Why This Matters in Large Vue Applications Small projects can survive without strict rules. Large projects usually can't. Consistency helps: onboard new developers faster simplify code reviews reduce technical debt prevent architectural drift improve long-term maintainability Instead of debating coding style in every pull request ESLint enforces it automatically. 🧪 Best Practices Treat ESLint as an architecture tool—not just a linter Enable Vue-specific rules from eslint-plugin-vue Agree on project conventions early Enforce Composition API patterns consistently Use no-restricted-imports to protect architectural boundaries Automate linting in CI\u002FCD pipelines Keep rules practical—avoid overcomplicating your configuration 📖 Learn more If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below: It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉 🧪 Advance skills A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success. Check out Certificates.dev by clicking this link or by clicking the image below: Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more! ✅ Summary ESLint is much more than a tool for catching syntax errors or enforcing formatting. As Vue applications grow, consistency becomes just as important as functionality. By using ESLint to enforce architectural decisions, you ensure that every new piece of code follows the same standards—making your project easier to understand, review, and maintain for years to come. Take care! And happy coding as always 🖥️","2026-07-06T12:00:09.479Z","019f374c-d2f0-769a-8a4f-180c7f565bd4","https:\u002F\u002Fmedia2.dev.to\u002Fdynamic\u002Fimage\u002Fwidth=1200,height=627,fit=cover,gravity=auto,format=auto\u002Fhttps%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevhhwr5be00v4h9dmmxi.png",false,true,"2026-07-06T08:38:53.000Z","enforce-better-vue-architecture-with-eslint","019d6bd6-7fe0-7244-80dc-9a4e8751886a","Jakub Andrzejewski","rss","This article discusses how ESLint can be leveraged to enforce a consistent architecture and coding standards in Vue applications. It highlights the importance of using ESLint beyond formatting, showcasing its ability to maintain project-wide patterns, improve code review processes, and ensure best practices in Vue development, particularly with the Composition API.","Enforce Better Vue Architecture with ESLint","2026-07-06T12:00:24.181Z","https:\u002F\u002Fdev.to\u002Fjacobandrewsky\u002Fenforce-better-vue-architecture-with-eslint-3fb0","5987f1e0425bcd09cd7e156b69730144717e56a57baa8243a9f5f2c7670170ef",[31,34,37,40,43],{"color":6,"id":32,"name":33,"slug":33},"019d6bd8-c9a8-7783-bd22-03145b355427","vue",{"color":6,"id":35,"name":36,"slug":36},"019f374d-0cc4-71ff-b906-ebaec8b0c343","eslint",{"color":6,"id":38,"name":39,"slug":39},"019d6bd8-ca21-71c5-a236-37d94fe57d24","composition-api",{"color":6,"id":41,"name":42,"slug":42},"019d9cf0-ca04-75bb-ad86-ce8da1c0be23","architecture",{"color":6,"id":7,"name":8,"slug":8},{"content":45,"createdAt":46,"id":47,"image":48,"isAffiliate":18,"isPublished":19,"publishedAt":49,"slug":50,"sourceId":51,"sourceName":52,"sourceType":24,"summary":53,"title":54,"updatedAt":55,"url":56,"urlHash":57,"tags":58},"This is the written-up version of the talk I gave at Vue MAD 2026 in Madrid: Clean Code Is Sexy Again. Making Your Vue Project AI-Ready. If you would rather watch it, here is the original recording on YouTube. Otherwise, read on. TLDR My main take: what is good for developers is also good for agents. There was never a separate “AI-ready” checklist. There was just good engineering, and now it pays off twice. “You don’t have to write code anymore” is half true. It only works if you know your stack and your codebase is built for an agent to work in. An agent is not magic. It is a loop: read context, pick a tool, run it, read the result, repeat. Once you have built one you stop asking “how does the magic work” and start asking “why does the same loop fly in one repo and fall apart in another.” Three things every Vue project should invest in: context (AGENTS.md, skills, hooks, a brain\u002F), feedback loops (types, lint, tests, a real browser), and discoverability (vertical feature slices). When the agent gets something wrong, fix the factory, not the PR. Add the lint rule, update AGENTS.md, tighten the prompt. The PR fix is one bug. The factory fix prevents the next hundred. The line everyone keeps repeating Everyone keeps saying the same thing: you don’t have to write code anymore. I think that’s half true. In some of my repos AI 10x’d me. In others it produced complete garbage. Same person, same agent, same model, different codebases. It only works if two things are true. First, you have real experience with the stack. I would not let an agent write Rust for me, because I have no idea what good Rust looks like and I could not review it. Second, the codebase has to be shaped so an agent can work in it. A greenfield project is easy. A brownfield project with no tests and 10,000-line components is a mess that AI makes worse, not better, until you clean it up. A good frame for “where are we” comes from Dan Shapiro’s post on the levels of AI-assisted development. It reads like driving automation. Level 0 is spicy autocomplete, the first Copilot. Then agents arrive and it gets a lot better. At the top is the software factory where the agent defines, ships, and fixes on its own. I put myself at level three. The agent writes most of the code, and I spend my time reviewing and reading every line. Sometimes I feel like the bottleneck. I would not recommend level four or five for anything serious yet, but the pace is real. Two weeks before the talk, the creator of Bun started using Claude Code to rewrite Bun from Zig to Rust. A million lines, in days, almost entirely by an agent. Triumph or disaster, I don’t know. What I know is this is the world we ship into. What an agent actually is Most Vue devs treat agents as a magic box. Magic is the wrong mental model. The best tip I can give you to get better at AI is to build your own coding agent once. Then it stops being magic, and you get a feel for why it does the right thing sometimes and the wrong thing other times. I wrote up the full build in Building Your Own Coding Agent From Scratch, but here is the whole picture in one image. The agent is bounded by three things: its context window, the tools you give it, and its ability to verify what it did. That is it. The analogy that unlocks everything is treating the agent like a new engineer joining your team. You would not throw a new hire into your worst legacy module and expect a feature in a week. Same with an agent. Strip the magic and a tool is just a function. Three fields: a description, a schema for the arguments, and the function that runs. type Tool = { description: string schema: Record&lt;string, string&gt; execute: (args: Record&lt;string, unknown&gt;) =&gt; Promise&lt;string&gt; } const TOOLS = new Map&lt;string, Tool&gt;([ ['read', { description: 'Read file with line numbers (not a directory)', schema: { path: 'string', offset: 'number?', limit: 'number?' }, execute: read, }], ['edit', { description: 'Replace old with new in file (old must be unique)', schema: { path: 'string', old: 'string', new: 'string' }, execute: edit, }], ['bash', { description: 'Run shell command', schema: { cmd: 'string' }, execute: bash, }], ]) Here is the part that demystifies everything: the model only ever sees the description and the schema. The execute function never leaves your machine. The model cannot run read(), it cannot even see it. So how does it “use” a tool? It reads the description and emits a request: “call read with path src\u002FApp.vue.” Your loop runs the real function and feeds the result back. Which means the description is the prompt. A vague description is a tool the model misuses. Tool descriptions are engineering, not docs. And the loop is just recursion. async function agentLoop(messages, systemPrompt, tools = TOOLS) { const response = await callApi(messages, systemPrompt, tools) const toolResults = await processToolCalls(response.content) const newMessages = [ ...messages, { role: 'assistant', content: response.content }, ] \u002F\u002F No tool calls → the agent is done if (toolResults.length === 0) return newMessages \u002F\u002F Tool calls → loop with results as the next user turn return agentLoop( [...newMessages, { role: 'user', content: toolResults }], systemPrompt, tools, ) } Call the API. The model replies with text and maybe some tool requests. Run each one, append the assistant reply and the tool results, and loop. When the model returns no tool calls, it is done. And messages is not a database or a session store. It is an array of { role, content }. That array is the agent’s memory. Every turn you append to it. I built a tiny version of this a few months ago and called it nanocode, around 350 lines of TypeScript. It is not perfect, but building it is what made the whole thing click. Once you see an agent this way, the question stops being “how does the magic work” and becomes “why does the same simple loop work brilliantly in one repo and fall apart in another?” That is the rest of this post. Clean code isn’t nice-to-have anymore I have been in plenty of projects where people told me “we don’t have time to write tests.” That argument is over. You do not need new patterns for AI. The patterns you already fight for in code review, the ones the senior dev keeps insisting on, are exactly the patterns that make agents work. A codebase that is hard for humans is hard for agents. Sprawl, hidden coupling, magical state: humans hate it, agents fail at it. In Vue, three things are worth investing in: context, feedback loops, and discoverability. Part 1: Context The agent is Leonard from Memento. Every new chat, the context resets. No long-term memory, no yesterday. So, like Leonard, it has to tattoo the rules where it will read them every single turn. That tattoo is AGENTS.md (or CLAUDE.md). It is the first thing you can optimize. But the tattoo space is finite, and most of it is already used before you write a word. Every model has a context window, and it works best when it is not full. Models degrade as the window fills: recall drops, reasoning slips, the agent starts confusing files. And the window is not empty when you start. The system prompt and the tool definitions already cost around 20k tokens before you type anything. AGENTS.md, skills, MCP servers, and sub-agents all spend from the same pool. In Claude Code you can see exactly where it goes by typing \u002Fcontext. This is why the biggest mistake with AGENTS.md is dumping everything into it: every coding rule, every bug post-mortem, every gotcha, 2000 lines that load on every single turn. A better shape is a thin doorway: # AGENTS.md Run `pnpm lint:fix &amp;&amp; pnpm typecheck` after changes. ## Stack Nuxt 4, @nuxt\u002Fcontent v3, @nuxt\u002Fui v3 ## Structure - `app\u002F` — Vue application - `content\u002F` — Markdown files ## Further reading **IMPORTANT:** read the relevant doc below before starting any task. - `docs\u002Fnuxt-content-gotchas.md` - `docs\u002Ftesting-strategy.md` The agent loads testing-strategy.md only when it writes a test, nuxt-content-gotchas.md only when it touches content. That is progressive disclosure: the right context at the right time. Two filters before a line goes into AGENTS.md. Can a tool enforce it? Then do not write prose about it. Is it universal or situational? Situational goes in docs\u002F. I go deeper on the AGENTS.md-versus-skills split in my Claude Code customization guide, and on why context fills up the way it does in reverse-engineering HumanLayer’s context engineering. brainmaxxing Forget looksmaxxing. We are brainmaxxing: maxing the one thing the agent has, its context. brainmaxxing is an open-source kit from a Cursor developer. It is a framework for giving an agent a documented memory, and it has three pieces. First, a brain\u002F folder. Plain markdown. The agent reads from it and writes back to it. There is one index.md with wiki-links describing the documentation that exists, and the rest is markdown files for codebase notes, plans, and principles. People found out you do not need RAG or anything complex. The simplest approach, wiki-links and markdown files, works best, and now the agent has a memory. (If you want the theory of why files beat a vector database here, I wrote it up in the four types of memory for coding agents.) Second, skills. A skill is like a prompt that lives in a markdown file with a name and a description. When you start Claude Code, only the name and description enter the context. When your prompt matches, the body loads. When the body calls a script, the script loads. Three levels of lazy loading, so skills scale. brainmaxxing’s \u002Freflect skill is a good example: at the end of a session it reviews the conversation and persists what mattered back into brain\u002F. So when the agent uses the wrong command and you correct it, you just say “remember this,” and it updates its own memory. Third, hooks. Vue has lifecycle hooks like onMounted. Claude Code has lifecycle hooks too. brainmaxxing wires a SessionStart hook that cats index.md into every new session, so the agent boots up already knowing the map. There are many more events. PreToolUse can block a destructive command or forbid reading your .env. PostToolUse can re-lint after an edit. SessionEnd could even send you a notification. I cover skills and hooks hands-on in my Vue workout tracker walkthrough. brainmaxxing also ships principles, around 16 markdown files describing how you want code written. One is boundary discipline: business logic lives in pure functions, the shell is thin and mechanical. The agent reads them and develops a better taste for your code. You can write your own too. Claude Code itself now ships something similar, called auto memory: it writes markdown notes about your project automatically. The catch is those files only live on your machine. That is exactly why I like keeping brain\u002F in Git. Check it in and the whole team’s agent boots up with the same memory on day one. The new hire on Monday starts where the team is, not from zero. Want VueUse-style code? Give the agent VueUse One more trick, and it is bigger than memory. You want the agent to use VueUse or Nuxt UI, but its training data is out of date and it does not know the current API. So give it the source. Clone the library into your repo as a git subtree: git subtree add --prefix=repos\u002Fvueuse \\ https:\u002F\u002Fgithub.com\u002Fvueuse\u002Fvueuse main --squash Then point AGENTS.md at it: ## Reference repositories - `repos\u002Fvueuse\u002F` — when writing new composables, mirror the patterns in `repos\u002Fvueuse\u002Fpackages\u002Fcore\u002Fuse*\u002F`. Now the agent’s pattern matching is anchored on real source instead of stale docs. Agents are post-trained on reading code, not prose. A subtree beats a submodule (no clone-time pain, just files) and beats pointing at node_modules (compiled and flattened, the structure is gone). One refinement: tell your editor to ignore repos\u002F so you do not accidentally auto-import VueUse internals, and have the agent distill what it learned into a short agent-patterns\u002Fvueuse.md so it does not re-explore 800 composables every session. This sounds stupid, but once you try it, it works far better than MCPs in my experience. The pattern comes from the Effect team’s post, The One Weird Git Trick That Makes Coding Agents More Effect-ive. Part 2: Feedback loops If an agent does not know when it broke something, it ships whatever compiles. So you give it deterministic ways to check its own work. Type safety is the first lie-detector. A strict tsconfig is non-negotiable, and noUncheckedIndexedAccess and exactOptionalPropertyTypes catch what default strict mode lets through. Types are a compile-time fiction, though, so parse with Zod or Valibot at every untyped boundary: fetch responses, route params, env vars, form input. Lint and format with Oxlint and Oxfmt. They are roughly 50x and 30x faster than ESLint and Prettier, fast enough to run on every save instead of batching at the end. Two Vue lint rules in particular keep components agent-friendly, because nobody, human or agent, can read a 3000-line component: 'vue\u002Fmax-template-depth': ['error', { maxDepth: 8 }] 'vue\u002Fmax-props': ['error', { maxProps: 6 }] A 12-prop component is a god object pretending to be one. I wrote up my full set in my opinionated ESLint setup for Vue projects, including custom local rules. Then tests. Vitest for units, and the big 2026 win, Vitest browser mode, which runs component tests in a real Chromium via Playwright instead of jsdom. Hover, focus, layout, scroll, all behave like production. The agent can finally trust that “passes in tests” means “works in a browser.” I go deep on this in the Vue testing pyramid with Vitest browser mode. Those are four layers. There are 11 more: API mocking, contract tests, E2E, accessibility, visual regression, performance, dead-code detection, and so on. Every one is a signal the agent can chase to green on its own. The cheap ones at the center run on every save, the expensive ones at the edge run in CI. What I now do in a new project is hand the agent my own write-up and say “set up the layers that make sense here.” The full breakdown lives in the modern frontend quality pipeline. The agent as a user Static checks can be green and the feature can still be broken. Did the modal open? Did the chart render? You need the agent to be the user. Vercel’s agent-browser is a browser CLI shaped for agents. One install and the agent has a real Chromium it can drive from the command line. npm i -g agent-browser agent-browser install agent-browser open localhost:5173 agent-browser snapshot -i # DOM tree with @refs agent-browser click @e2 # click by ref agent-browser screenshot --annotate agent-browser console # read page console The snapshot returns the DOM as plain text with refs, the agent clicks by ref, screenshots come back annotated, console errors come back as text. So when there is a bug, you say “reproduce it with agent-browser,” and once the agent can reproduce something, fixing it is easy. The same thing a good developer would do. For this to work the app has to be agent-runnable: the dev server is one command, the port is stable, there is a way to know the server is ready, and test data is seeded so the agent does not hit a login wall. Then “types pass” is no longer the bar. “Works in a browser” is. I built a full version of this into CI in automated QA with Claude Code, agent-browser, and GitHub Actions. Finally, put a gate at commit time. Lefthook (or Husky) runs lint, typecheck, and the related tests on every commit, so bad code does not reach main. I prefer Lefthook because it runs jobs in parallel and the config is YAML the agent can read and edit, not a shell-script soup. The one rule: no --no-verify escape hatch. The whole point of the gate is that it does not open. # lefthook.yml pre-commit: parallel: true jobs: - run: pnpm oxlint - run: pnpm vue-tsc --build - run: pnpm vitest related --run {staged_files} Part 3: Discoverability Most Vue apps start flat. You run create vue and you get components\u002F, composables\u002F, stores\u002F, views\u002F, router\u002F. Everything is grouped by what a file is. That is fine for small apps, but in a real application that components\u002F folder grows to 80+ files, and a single “checkout” change is scattered across three folders. The agent has the same problem you do: it greps across the whole tree and most of what it pulls back is noise. Feature slicing groups by what files do, not by what they are. First you find the domains. Imagine a workout tracker: the domains are workout, timers, exercises, and settings. Then each domain owns its own components, composables, and store: src\u002Ffeatures\u002F ├── workout\u002F │ ├── components\u002F │ ├── composables\u002F │ └── store.ts ├── timers\u002F ├── exercises\u002F └── settings\u002F Finding the domains is the hard part on a big codebase, but once you have them, “change the settings page” means opening one folder. Same files, same code, different addressing. And the win for the agent is concrete. Ask it to “add a streak counter to the active workout.” In a flat layout it greps components\u002F and composables\u002F and pulls back SettingsForm, TimerDisplay, useSettings, maybe 40% relevant. In a feature layout it lists features\u002Fworkout\u002F and gets exactly the files that matter, 100% relevant. No grep, no guess, tokens go to output instead of search. Folders alone do not keep the agent honest, so add three import rules. Arrows only point down the layers. Sibling features never import each other; if they share something, it moves to a shared layer like components\u002F or lib\u002F. And a page that uses two features composes them at the page level rather than letting one feature reach into another. The most important rule is the second one: features stay decoupled. As a bonus, a codebase split this way is already shaped for a micro-frontend approach if you ever need it. Where this is heading AFK coding (away from keyboard) is already here. You write a spec, kick off an agent, and walk away. It runs the tests, hits a failure, fixes itself, runs again, and opens a draft PR with a summary. You review. The work is no longer “me typing.” It is “me deciding what should exist, and reviewing what came back.” The loop in practice has humans at the edges and agents in the middle. You align with the business on the spec, the agent breaks the big ticket into vertical sub-tickets, one agent works each slice with TDD inside, a dedicated refactor pass runs (the step LLMs always skip), a QA agent drives the real browser, and then you read the PR. I wrote the long version in how to do AFK coding. The most important thing in this loop is what you do when it goes wrong. The agent ships a bug, and the instinct is to fix the bug, merge, and move on. That instinct is wrong. Fix the factory, not the PR. A bug is not a bug, it is a factory defect. Add an ESLint rule that catches that whole class of mistake. Update AGENTS.md so the convention is written down. Tighten the slash command or the prompt if that is where it leaked. The PR fix is one bug. The factory fix prevents the next hundred. And if the codebase is messy in the first place, use the agent to refactor it before you expect any of this to work. Every PR review teaches the factory, and the codebase gets smarter over time. As a test of all this, the week before the talk I tried to port React Ink (declarative components for terminal UIs) to Vue, using AI only. It worked surprisingly well. The approach combined everything above. I vendored Ink, Vue core, and VueUse into repos\u002F as read-only source. I used a brain\u002F vault for gotchas and an api-tracker. And because React Ink has a lot of tests, I ported the tests first: each Ava scenario became a Vitest test at the behavior level, not the React implementation. Run it, red, no implementation yet. Then translate the implementation, JSX to SFC, hooks to composables. Run it, green. Then reflect the learnings back into brain\u002F so the loop sharpens itself. I would not publish it as a real library without going back and understanding every line first, but as a proof that you can port a well-tested React library to Vue mostly AFK, it holds up. I even added a Stop hook that fires when a turn ends and spawns a second, headless Claude in the background to read the session transcript and write only new durable learnings into brain\u002F. The one gotcha is that the child also ends a turn, which would fire the hook forever, so an env flag marks the child and the hook bails when it sees it. The project updates its own docs while I get coffee. The twist This talk was not really about AI. I tricked you. None of it is new. Testing, TDD, feature-based architecture, strict types, small components: these are the things senior engineers have fought for in code review for twenty years. They were good before a single agent existed. The only thing that changed is the payoff. The same discipline that used to make a codebase pleasant for the next human now also makes it tractable for an agent, so every hour you spent caring about architecture quietly started earning twice. That is the whole bet. Tight context, plenty of feedback loops, real discoverability. Which of them is your project missing? Come find me and tell me what your AGENTS.md looks like.","2026-06-27T16:00:02.356Z","019f09cf-3518-7096-8fef-95033e6e53c2","https:\u002F\u002Fi.ytimg.com\u002Fvi\u002F9bKMqvFRvvI\u002Fhqdefault.jpg","2026-06-27T00:00:00.000Z","clean-code-is-sexy-again-making-your-vue-project-ai-ready","019d70dd-e3e7-76db-84a4-87b896dea004","alexop.dev","The article discusses the importance of clean code in making Vue projects ready for AI integration, emphasizing that good engineering practices benefit both developers and AI agents. It highlights the need for context, feedback loops, and discoverability in projects to enable effective AI assistance, while cautioning that AI's effectiveness depends on the developer's familiarity with the stack and the structure of the codebase.","Clean Code Is Sexy Again: Making Your Vue Project AI-Ready","2026-06-29T20:00:17.397Z","https:\u002F\u002Falexop.dev\u002Fposts\u002Fclean-code-is-sexy-again-vue-ai-ready\u002F","308ed1fc6ac401b781ff16aa2e321e0a152bfe1110030261e6875e67b16f015c",[59,60,63,66,69],{"color":6,"id":32,"name":33,"slug":33},{"color":6,"id":61,"name":62,"slug":62},"019d9d20-e07b-7685-9ebb-fae0b963f243","ai",{"color":6,"id":64,"name":65,"slug":65},"019f09cf-5bcd-751c-8d46-9e123bd784af","clean-code",{"color":6,"id":67,"name":68,"slug":68},"019dafe9-8a6d-7218-81f1-37052cbe9b78","development",{"color":6,"id":7,"name":8,"slug":8},{"content":71,"createdAt":72,"id":73,"image":74,"isAffiliate":18,"isPublished":19,"publishedAt":75,"slug":76,"sourceId":22,"sourceName":23,"sourceType":24,"summary":77,"title":78,"updatedAt":79,"url":80,"urlHash":81,"tags":82},"Performance has become one of the most important aspects of modern frontend development. Users expect websites to be fast and Google rewards fast websites. And yet... even experienced Vue developers still introduce performance issues that can significantly impact user experience. The tricky part? Most applications work perfectly fine during development. Problems usually appear later: larger datasets slower devices poor network conditions increased application complexity In this article, we'll look at 10 common Vue performance mistakes I still see in production applications and how to fix them. Let's dive in. 🤔 Why Vue Performance Matters Vue is already a highly optimized framework. However, even the best framework cannot compensate for inefficient application code. Poor performance can lead to: slow page loads delayed interactions poor Core Web Vitals scores increased battery usage frustrated users The good news? Most performance issues can be fixed with relatively small changes. 🟢 Mistake #1: Using Deep Watchers Everywhere A common mistake is enabling deep watchers on large objects. Example: watch( userData, () =&gt; { saveDraft() }, { deep: true } ) Deep watchers force Vue to traverse the entire object tree. For large datasets this can become very expensive. Instead: watch specific properties split large objects use computed values when possible 🟢 Mistake #2: Making Everything Reactive Not every piece of data needs reactivity. I often see code like this: const hugeDataset = ref(largeArray) When the data rarely changes, Vue still needs to create reactive proxies. For large collections this introduces unnecessary overhead. A better approach: const hugeDataset = shallowRef(largeArray) Or even: const hugeDataset = markRaw(largeArray) when reactivity isn't needed at all. 🟢 Mistake #3: Creating New Objects Inside Computed Properties Consider this: const userInfo = computed(() =&gt; ({ name: user.value.name, role: user.value.role })) A brand-new object is created every time the computed runs. This can trigger unnecessary component updates. Instead, prefer returning primitives when possible or memoizing expensive transformations. 🟢 Mistake #4: Using v-if Instead of v-show for Frequently Toggled Elements Many developers use: &lt;div v-if=\"isOpen\"&gt; Content &lt;\u002Fdiv&gt; But if the element is shown and hidden frequently, Vue must repeatedly: mount render destroy A better option: &lt;div v-show=\"isOpen\"&gt; Content &lt;\u002Fdiv&gt; This simply toggles CSS visibility. For frequently toggled UI elements, it's usually much faster. 🟢 Mistake #5: Rendering Huge Lists Without Virtualization Rendering thousands of DOM nodes is expensive. Example: &lt;div v-for=\"user in users\" :key=\"user.id\" &gt; {{ user.name }} &lt;\u002Fdiv&gt; This might work with 100 items. It won't feel great with 10,000. Instead consider: virtual scrolling pagination infinite loading Libraries like Vue Virtual Scroller can dramatically improve performance. 🟢 Mistake #6: Lazy Loading Nothing Many applications ship their entire codebase on the first page load. Example: import UserSettings from '.\u002FUserSettings.vue' This increases: bundle size download time parse time Instead: const UserSettings = defineAsyncComponent( () =&gt; import('.\u002FUserSettings.vue') ) Users only download code when it's actually needed. 🟢 Mistake #7: Fetching Data Sequentially A surprisingly common issue: const users = await fetchUsers() const posts = await fetchPosts() const comments = await fetchComments() Each request waits for the previous one. A faster approach: const [users, posts, comments] = await Promise.all([ fetchUsers(), fetchPosts(), fetchComments() ]) This can reduce loading times significantly. 🟢 Mistake #8: Forgetting About Image Optimization Images are often the largest assets on a page. Yet many applications still serve: oversized images uncompressed formats images outside the viewport For Vue and Nuxt applications: use WebP or AVIF lazy load images generate responsive sizes Image optimization frequently provides the biggest performance wins. 🟢 Mistake #9: Ignoring Component Re-Renders A component may render far more often than expected. For example: &lt;ExpensiveChart :data=\"chartData\" \u002F&gt; If chartData changes reference on every update, the chart keeps re-rendering. Common solutions: stabilize references use shallowRef avoid unnecessary reactive updates profile components with Vue DevTools Small changes here can have a huge impact. 🟢 Mistake #10: Never Measuring Performance The biggest mistake? Not measuring anything. Many teams optimize blindly. Instead, regularly check: Lighthouse Core Web Vitals Vue DevTools Chrome Performance Panel Network waterfalls Performance work should be data-driven. You can't improve what you don't measure. 🟢 Performance Checklist Before shipping a Vue application, ask yourself: ✅ Am I using deep watchers only when necessary? ✅ Do all objects really need reactivity? ✅ Are large lists virtualized? ✅ Are routes and components lazy loaded? ✅ Are API requests running in parallel? ✅ Are images optimized? ✅ Have I measured actual performance? If any answer is \"no\", there may be easy performance wins available. 🧪 Best Practices Use shallowRef for large datasets Avoid deep watchers whenever possible Lazy load routes and heavy components Virtualize large lists Optimize images aggressively Profile real-world user flows Monitor Core Web Vitals Measure before and after every optimization 📖 Learn more If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below: It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉 🧪 Advance skills A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success. Check out Certificates.dev by clicking this link or by clicking the image below: Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more! ✅ Summary Vue is fast by default. But performance problems often come from how we use the framework rather than the framework itself. In this article, you learned: 10 common Vue performance mistakes Why they impact real-world applications How to identify them Practical ways to fix them Best practices for building faster Vue apps Many of these issues are easy to overlook during development but become expensive at scale. By avoiding these mistakes and measuring performance regularly, you can build applications that feel fast, responsive, and enjoyable to use. Take care! And happy coding as always 🖥️","2026-06-15T08:00:09.697Z","019eca4b-8dce-754e-ad17-a0f9b1aef338","https:\u002F\u002Fmedia2.dev.to\u002Fdynamic\u002Fimage\u002Fwidth=1200,height=627,fit=cover,gravity=auto,format=auto\u002Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fidhps7seqfikj9j24oz3.png","2026-06-15T07:08:48.000Z","10-vue-performance-mistakes-i-still-see-in-production-apps","This article discusses ten common performance mistakes that Vue developers often make in production applications, highlighting issues such as using deep watchers unnecessarily and making everything reactive. It emphasizes the importance of optimizing performance to enhance user experience and offers practical solutions to improve application efficiency.","10 Vue Performance Mistakes I Still See in Production Apps","2026-06-15T08:00:29.794Z","https:\u002F\u002Fdev.to\u002Fjacobandrewsky\u002F10-vue-performance-mistakes-i-still-see-in-production-apps-52a1","4bad7a0b92210da78523e2dbba139490f2eade4aa9b344635bb1bb7b69b6959a",[83,84,87,90,93],{"color":6,"id":32,"name":33,"slug":33},{"color":6,"id":85,"name":86,"slug":86},"019d6bd8-ca26-775c-b9b5-c3439dbe5789","performance",{"color":6,"id":88,"name":89,"slug":89},"019e3af5-4a35-705c-8ab1-4404b653e0e8","optimization",{"color":6,"id":91,"name":92,"slug":92},"019daac3-2f85-7393-b891-9da3891267ca","reactivity",{"color":6,"id":7,"name":8,"slug":8},{"content":95,"createdAt":96,"id":97,"image":98,"isAffiliate":18,"isPublished":19,"publishedAt":99,"slug":100,"sourceId":22,"sourceName":23,"sourceType":24,"summary":101,"title":102,"updatedAt":103,"url":104,"urlHash":105,"tags":106},"When developers think about performance optimization, they usually focus on things like: lazy loading caching image optimization bundle size And while those things absolutely matter there’s another area that heavily impacts user experience -&gt; Loading states and layout stability. A badly implemented loading experience can make an app feel slow, jumpy, or frustrating to use. This is strongly connected to an important Core Web Vital metric Cumulative Layout Shift (CLS). In this article, we’ll explore: What CLS is Why loaders are critical for perceived performance How poor loading states hurt UX Practical examples in Vue Best practices for stable layouts Let’s dive in. 🤔 What Is Cumulative Layout Shift (CLS)? CLS measures how much elements unexpectedly move during page loading. Example of bad CLS: Text suddenly jumps down Buttons move while you try to click Images appear late and push content around We’ve all experienced websites like this: 👉 You try to click something… and suddenly the layout shifts. Extremely annoying. Why does this happen? Usually because: content loads asynchronously elements have no reserved space loaders are missing images don’t define dimensions components suddenly appear Why does CLS matter? Because it affects user experience, accessibility, or mobile usability (and obviously Google Core Web Vitals). Even if your app is technically fast poor layout stability can make it feel slow. 👉 Users care more about perceived performance than actual milliseconds. A good loader communicates progress, prevents layout jumping, and makes apps feel responsive A bad or missing loader creates uncertainty. Users start thinking: “Did the app freeze?” “Is something broken?” “Why is everything moving?” 🟢 Implementing proper loaders in Vue Let's take a look at the following example: &lt;script setup lang=\"ts\"&gt; const users = ref([]) const loading = ref(true) onMounted(async () =&gt; { users.value = await fetchUsers() loading.value = false }) &lt;\u002Fscript&gt; &lt;template&gt; &lt;div v-if=\"loading\" class=\"skeleton-list\"&gt; &lt;div v-for=\"n in 5\" :key=\"n\" class=\"skeleton-card\" \u002F&gt; &lt;\u002Fdiv&gt; &lt;UserCard v-else v-for=\"user in users\" :key=\"user.id\" :user=\"user\" \u002F&gt; &lt;\u002Ftemplate&gt; &lt;style scoped&gt; .skeleton-card { height: 120px; border-radius: 12px; margin-bottom: 16px; } &lt;\u002Fstyle&gt; We fetch users, but when the fetch is in progress we display the same hard coded number of loaders\u002Fskeletons. When the users are loaded there is no layout shift as it occupies the same space improving perceived performance and User Experience. If we don't know how many results there will be, we have to assume some number but it is still better than not having skeletons at all :) Many apps still use simple spinners or text\u002Ficon loaders like: &lt;div&gt;Loading...&lt;\u002Fdiv&gt; But modern UX usually prefers Skeleton loaders because they mimic final layout, reduce layout shift, and improve perceived speed. 🧪 Best Practices Prefer skeleton loaders over tiny spinners Reserve space before content loads Keep loading and final layouts similar Always define image dimensions Avoid injecting large content suddenly Test CLS using Lighthouse or Core Web Vitals tools Think about perceived performance — not just raw speed 📖 Learn more If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below: It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉 🧪 Advance skills A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success. Check out Certificates.dev by clicking this link or by clicking the image below: Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more! ✅ Summary Loaders are much more important than most developers realize. In this article, you learned: What Cumulative Layout Shift (CLS) is Why poor loading states hurt UX How skeleton loaders improve perceived performance How to avoid layout jumping in Vue and other frameworks Best practices for stable, responsive interfaces Fast apps are great. But apps that feel smooth and stable are what users truly remember. Take care! And happy coding as always 🖥️","2026-05-11T12:00:12.770Z","019e16e8-bfd2-7191-94bc-00d70b24a540","https:\u002F\u002Fmedia2.dev.to\u002Fdynamic\u002Fimage\u002Fwidth=1200,height=627,fit=cover,gravity=auto,format=auto\u002Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbym0bvla6v4jibn66jc3.png","2026-05-11T09:29:43.000Z","why-loaders-matter-for-performance","This article discusses the importance of loaders in enhancing perceived performance and user experience in web applications, particularly focusing on the concept of Cumulative Layout Shift (CLS). It highlights how proper implementation of loaders in Vue can prevent layout shifts and improve user engagement by providing a stable loading experience. The article also offers practical examples and best practices for implementing effective loaders.","Why Loaders Matter for Performance","2026-06-10T14:35:13.764Z","https:\u002F\u002Fdev.to\u002Fjacobandrewsky\u002Fwhy-loaders-matter-for-performance-4b8a","d90fb6c45448cd6496f59658878a5381e26f71ac1b914ec7c7c4c29bf64f2c6e",[107,108,109,112,115],{"color":6,"id":32,"name":33,"slug":33},{"color":6,"id":85,"name":86,"slug":86},{"color":6,"id":110,"name":111,"slug":111},"019e16e8-dbfa-76d6-bb2d-793aee049186","loading",{"color":6,"id":113,"name":114,"slug":114},"019e16e8-dc00-714d-911a-a5bf39238587","user-experience",{"color":6,"id":7,"name":8,"slug":8},1,20,{"tags":119},[120,125,129,132,136,140,144,146,150,154,158,162,167,169,173,177,181,185,187,191,195,199,203,207,211,215,219,223,225,229,233,237,241,245,249,253,255,259,263,267,271,273,277,281,285,289,293,297,301,305,307,311,315,319,323,327,331,335,339,343,347,351,356,360,364,368,372,374,378,381,385,389,393,397,401,405,407,411,415,419,423,427,431,435,439,443,447,451,456,460,464,468,472,476,480,484,488,492,497,501,505,507,511,515,519,523,527,531,534,538,542,547],{"articleCount":121,"color":6,"createdAt":122,"id":123,"name":124,"slug":124,"updatedAt":122},0,"2026-04-30T04:19:02.605Z","019ddc9c-954c-7703-8288-76d562fec577","accelerator",{"articleCount":116,"color":6,"createdAt":126,"id":127,"name":128,"slug":128,"updatedAt":126},"2026-04-08T06:47:43.120Z","019d6bd8-caef-76b9-bacd-363e31e6d2a9","accessibility",{"articleCount":130,"color":6,"createdAt":131,"id":61,"name":62,"slug":62,"updatedAt":131},8,"2026-04-17T20:27:50.780Z",{"articleCount":121,"color":6,"createdAt":133,"id":134,"name":135,"slug":135,"updatedAt":133},"2026-05-05T00:00:19.031Z","019df56f-8257-752e-a918-cdbb07c32b86","ai-agents",{"articleCount":116,"color":6,"createdAt":137,"id":138,"name":139,"slug":139,"updatedAt":137},"2026-06-01T12:00:14.713Z","019e830e-5378-722b-929b-a57d67bcf605","animation",{"articleCount":5,"color":6,"createdAt":141,"id":142,"name":143,"slug":143,"updatedAt":141},"2026-04-08T06:47:55.499Z","019d6bd8-fb46-77ef-a3b6-bd2d30ab8919","api",{"articleCount":130,"color":6,"createdAt":145,"id":41,"name":42,"slug":42,"updatedAt":145},"2026-04-17T19:35:19.300Z",{"articleCount":121,"color":6,"createdAt":147,"id":148,"name":149,"slug":149,"updatedAt":147},"2026-04-23T12:00:11.615Z","019dba36-435e-765d-bfb2-c5be05362c27","astro",{"articleCount":121,"color":6,"createdAt":151,"id":152,"name":153,"slug":153,"updatedAt":151},"2026-06-01T20:00:15.958Z","019e84c5-cc55-7377-9e5d-77240ea8a880","authentication",{"articleCount":121,"color":6,"createdAt":155,"id":156,"name":157,"slug":157,"updatedAt":155},"2026-05-05T00:00:19.020Z","019df56f-824c-7031-9e34-2f47ad139eb6","automation",{"articleCount":121,"color":6,"createdAt":159,"id":160,"name":161,"slug":161,"updatedAt":159},"2026-06-11T16:00:12.019Z","019eb769-9af2-7471-b482-3f1a404f802e","azure",{"articleCount":163,"color":6,"createdAt":164,"id":165,"name":166,"slug":166,"updatedAt":164},2,"2026-04-17T20:27:50.776Z","019d9d20-e077-71cc-a605-8ac2a1566143","backend",{"articleCount":5,"color":6,"createdAt":168,"id":7,"name":8,"slug":8,"updatedAt":168},"2026-04-08T06:47:56.976Z",{"articleCount":121,"color":6,"createdAt":170,"id":171,"name":172,"slug":172,"updatedAt":170},"2026-06-06T00:00:11.711Z","019e9a3a-e5be-74b3-a01a-92c1f9427a2c","beta",{"articleCount":121,"color":6,"createdAt":174,"id":175,"name":176,"slug":176,"updatedAt":174},"2026-06-10T13:53:29.711Z","019eb1cf-3e6e-70f0-a761-0fb9b61d5675","budgeting",{"articleCount":116,"color":6,"createdAt":178,"id":179,"name":180,"slug":180,"updatedAt":178},"2026-06-16T16:11:31.438Z","019ed133-c4ee-70bb-8e21-7dc86e8adee4","bun",{"articleCount":121,"color":6,"createdAt":182,"id":183,"name":184,"slug":184,"updatedAt":182},"2026-05-19T20:00:14.559Z","019e41d3-1ade-77b0-9e4f-e9b97a6361ca","cdn",{"articleCount":116,"color":6,"createdAt":186,"id":64,"name":65,"slug":65,"updatedAt":186},"2026-06-27T16:00:12.237Z",{"articleCount":116,"color":6,"createdAt":188,"id":189,"name":190,"slug":190,"updatedAt":188},"2026-05-16T18:48:40.777Z","019e321e-8248-75cc-9b69-59c2db6dd846","cli",{"articleCount":121,"color":6,"createdAt":192,"id":193,"name":194,"slug":194,"updatedAt":192},"2026-05-05T00:00:19.014Z","019df56f-8246-747f-be4b-a716863a93ea","cloud-platform",{"articleCount":116,"color":6,"createdAt":196,"id":197,"name":198,"slug":198,"updatedAt":196},"2026-06-16T16:11:31.289Z","019ed133-c458-74d8-a5c9-654f77837e7c","cloudflare",{"articleCount":116,"color":6,"createdAt":200,"id":201,"name":202,"slug":202,"updatedAt":200},"2026-07-27T18:11:45.304Z","019fa4c6-93c3-76ea-baa3-7d5fe541ac5b","cms",{"articleCount":116,"color":6,"createdAt":204,"id":205,"name":206,"slug":206,"updatedAt":204},"2026-04-30T04:19:02.045Z","019ddc9c-931c-743d-a1cb-e4449630fe47","collaboration",{"articleCount":116,"color":6,"createdAt":208,"id":209,"name":210,"slug":210,"updatedAt":208},"2026-06-10T13:53:29.567Z","019eb1cf-3dde-776d-9398-4863764f9ac3","community",{"articleCount":116,"color":6,"createdAt":212,"id":213,"name":214,"slug":214,"updatedAt":212},"2026-05-06T16:00:17.128Z","019dfe04-bee8-7384-bc58-a712f677807c","comparison",{"articleCount":116,"color":6,"createdAt":216,"id":217,"name":218,"slug":218,"updatedAt":216},"2026-05-14T12:00:21.354Z","019e265b-f569-71d8-a02a-a17ec8180644","component-design",{"articleCount":116,"color":6,"createdAt":220,"id":221,"name":222,"slug":222,"updatedAt":220},"2026-07-18T20:00:18.730Z","019f76d0-bb29-72d6-8d0b-05ffd1d3c8ed","composables",{"articleCount":5,"color":6,"createdAt":224,"id":38,"name":39,"slug":39,"updatedAt":224},"2026-04-08T06:47:42.915Z",{"articleCount":116,"color":6,"createdAt":226,"id":227,"name":228,"slug":228,"updatedAt":226},"2026-06-27T12:00:13.113Z","019f08f3-a538-74ed-82c5-038edce7100a","copilot",{"articleCount":121,"color":6,"createdAt":230,"id":231,"name":232,"slug":232,"updatedAt":230},"2026-04-25T12:00:12.674Z","019dc482-ff81-73ac-9b1c-6ad47f0afde0","data-management",{"articleCount":121,"color":6,"createdAt":234,"id":235,"name":236,"slug":236,"updatedAt":234},"2026-06-04T20:00:17.367Z","019e9438-e5d7-731f-bf47-8dcd86c6655c","data-privacy",{"articleCount":116,"color":6,"createdAt":238,"id":239,"name":240,"slug":240,"updatedAt":238},"2026-05-05T12:00:17.083Z","019df802-a8bb-775a-b843-93d883c7dfc5","data-science",{"articleCount":121,"color":6,"createdAt":242,"id":243,"name":244,"slug":244,"updatedAt":242},"2026-06-11T16:00:12.028Z","019eb769-9afb-7709-8a67-2a12f5e557c7","deepseek",{"articleCount":116,"color":6,"createdAt":246,"id":247,"name":248,"slug":248,"updatedAt":246},"2026-05-25T08:00:12.834Z","019e5e26-0e21-7366-87c7-0b1334180b0a","dependency-cruiser",{"articleCount":116,"color":6,"createdAt":250,"id":251,"name":252,"slug":252,"updatedAt":250},"2026-04-18T04:30:00.710Z","019d9eda-5005-7329-a463-189572e25635","deployment",{"articleCount":163,"color":6,"createdAt":254,"id":67,"name":68,"slug":68,"updatedAt":254},"2026-04-21T12:00:11.373Z",{"articleCount":116,"color":6,"createdAt":256,"id":257,"name":258,"slug":258,"updatedAt":256},"2026-06-29T08:00:12.101Z","019f1264-9f44-762d-b5fd-837839fdc586","devtools",{"articleCount":121,"color":6,"createdAt":260,"id":261,"name":262,"slug":262,"updatedAt":260},"2026-05-29T20:00:13.197Z","019e7552-ad8c-731f-ad8b-49253ed0e1b9","docker",{"articleCount":116,"color":6,"createdAt":264,"id":265,"name":266,"slug":266,"updatedAt":264},"2026-06-27T12:00:13.081Z","019f08f3-a518-7607-aa8c-782b4f10c2ed","documentation",{"articleCount":116,"color":6,"createdAt":268,"id":269,"name":270,"slug":270,"updatedAt":268},"2026-04-30T04:19:02.055Z","019ddc9c-9327-744f-a2da-31b64c7b6ba4","editor",{"articleCount":116,"color":6,"createdAt":272,"id":35,"name":36,"slug":36,"updatedAt":272},"2026-07-06T12:00:24.261Z",{"articleCount":121,"color":6,"createdAt":274,"id":275,"name":276,"slug":276,"updatedAt":274},"2026-05-02T00:00:23.123Z","019de5fc-7e52-740a-807c-d322c373865b","firewall",{"articleCount":116,"color":6,"createdAt":278,"id":279,"name":280,"slug":280,"updatedAt":278},"2026-05-06T16:00:17.134Z","019dfe04-beee-7481-b8e7-4034640e840a","frameworks",{"articleCount":121,"color":6,"createdAt":282,"id":283,"name":284,"slug":284,"updatedAt":282},"2026-04-08T06:47:56.883Z","019d6bd9-00b2-7199-8e88-2530ef258032","generics",{"articleCount":116,"color":6,"createdAt":286,"id":287,"name":288,"slug":288,"updatedAt":286},"2026-07-27T18:11:45.390Z","019fa4c6-9419-7657-844e-58ec868ed664","git",{"articleCount":163,"color":6,"createdAt":290,"id":291,"name":292,"slug":292,"updatedAt":290},"2026-07-15T00:00:22.207Z","019f6313-12bf-7151-81f2-c8b43b09d979","html",{"articleCount":121,"color":6,"createdAt":294,"id":295,"name":296,"slug":296,"updatedAt":294},"2026-05-06T00:00:17.012Z","019dfa95-d673-71ef-be4a-8761512ffe5c","infrastructure",{"articleCount":116,"color":6,"createdAt":298,"id":299,"name":300,"slug":300,"updatedAt":298},"2026-06-16T16:11:31.264Z","019ed133-c43f-704f-8c1a-fbc299c8a3e5","javascript",{"articleCount":116,"color":6,"createdAt":302,"id":303,"name":304,"slug":304,"updatedAt":302},"2026-07-13T08:00:18.266Z","019f5a7d-bf5a-76e0-903a-c87435cc3e09","lazy-loading",{"articleCount":116,"color":6,"createdAt":306,"id":110,"name":111,"slug":111,"updatedAt":306},"2026-05-11T12:00:19.963Z",{"articleCount":121,"color":6,"createdAt":308,"id":309,"name":310,"slug":310,"updatedAt":308},"2026-04-25T12:00:12.682Z","019dc482-ff8a-7698-8af5-95db54a26d6b","local-first",{"articleCount":116,"color":6,"createdAt":312,"id":313,"name":314,"slug":314,"updatedAt":312},"2026-05-14T12:00:21.370Z","019e265b-f579-71cd-84b2-ac385b5225ae","maintainability",{"articleCount":116,"color":6,"createdAt":316,"id":317,"name":318,"slug":318,"updatedAt":316},"2026-05-10T16:00:18.613Z","019e129e-34b4-7107-acfb-27f6b8604eb0","management",{"articleCount":116,"color":6,"createdAt":320,"id":321,"name":322,"slug":322,"updatedAt":320},"2026-06-27T12:00:13.065Z","019f08f3-a508-7334-aa03-12b281698ea8","markdown",{"articleCount":116,"color":6,"createdAt":324,"id":325,"name":326,"slug":326,"updatedAt":324},"2026-07-28T12:00:27.878Z","019fa899-02e6-758e-bdcb-8a4a923507df","mcp",{"articleCount":116,"color":6,"createdAt":328,"id":329,"name":330,"slug":330,"updatedAt":328},"2026-06-17T12:00:12.439Z","019ed574-0a96-7485-9c90-522e4be3e092","meta-tags",{"articleCount":121,"color":6,"createdAt":332,"id":333,"name":334,"slug":334,"updatedAt":332},"2026-05-26T08:00:14.598Z","019e634c-7105-7073-bc86-c32fa0a4401b","microfrontends",{"articleCount":121,"color":6,"createdAt":336,"id":337,"name":338,"slug":338,"updatedAt":336},"2026-05-19T16:00:13.516Z","019e40f7-5ccc-729c-92ba-bcc0aad8a16f","microvm",{"articleCount":121,"color":6,"createdAt":340,"id":341,"name":342,"slug":342,"updatedAt":340},"2026-05-05T00:00:19.025Z","019df56f-8250-768c-a659-b9f524ff902c","multi-tenant",{"articleCount":121,"color":6,"createdAt":344,"id":345,"name":346,"slug":346,"updatedAt":344},"2026-05-08T04:00:17.998Z","019e05be-4c4d-759e-a51e-8ac760f82f6d","nextjs",{"articleCount":116,"color":6,"createdAt":348,"id":349,"name":350,"slug":350,"updatedAt":348},"2026-04-17T19:35:19.293Z","019d9cf0-c9fc-76a0-8c4e-b818a89c3774","nitro",{"articleCount":352,"color":6,"createdAt":353,"id":354,"name":355,"slug":355,"updatedAt":353},27,"2026-04-08T06:47:55.381Z","019d6bd8-fad3-70a9-a74d-ab96e3a2f45d","nuxt",{"articleCount":116,"color":6,"createdAt":357,"id":358,"name":359,"slug":359,"updatedAt":357},"2026-04-30T04:19:02.038Z","019ddc9c-9315-735e-a7e8-8424790e8859","nuxt-ui",{"articleCount":116,"color":6,"createdAt":361,"id":362,"name":363,"slug":363,"updatedAt":361},"2026-06-08T12:00:16.030Z","019ea71a-dc9d-7607-9cfa-df0f31ab5876","observability",{"articleCount":121,"color":6,"createdAt":365,"id":366,"name":367,"slug":367,"updatedAt":365},"2026-05-04T20:00:20.503Z","019df493-ce17-76ed-bd80-2a3914e0f409","open-source",{"articleCount":116,"color":6,"createdAt":369,"id":370,"name":371,"slug":371,"updatedAt":369},"2026-06-08T12:00:16.022Z","019ea71a-dc95-72b8-a7e3-70f9e2ac3710","opentelemetry",{"articleCount":5,"color":6,"createdAt":373,"id":88,"name":89,"slug":89,"updatedAt":373},"2026-05-18T12:00:14.389Z",{"articleCount":116,"color":6,"createdAt":375,"id":376,"name":377,"slug":377,"updatedAt":375},"2026-05-28T20:00:13.016Z","019e702c-50d7-74eb-8849-8b1cebcf411e","orchestration",{"articleCount":379,"color":6,"createdAt":380,"id":85,"name":86,"slug":86,"updatedAt":380},16,"2026-04-08T06:47:42.920Z",{"articleCount":116,"color":6,"createdAt":382,"id":383,"name":384,"slug":384,"updatedAt":382},"2026-06-10T13:53:29.575Z","019eb1cf-3de7-7326-87d9-c55ad1c0fa39","personalization",{"articleCount":116,"color":6,"createdAt":386,"id":387,"name":388,"slug":388,"updatedAt":386},"2026-04-17T19:35:19.263Z","019d9cf0-c9de-70b2-9057-76d771c3379e","pinia",{"articleCount":121,"color":6,"createdAt":390,"id":391,"name":392,"slug":392,"updatedAt":390},"2026-05-02T00:00:23.112Z","019de5fc-7e47-7075-8aeb-9e90f7689f57","postgres",{"articleCount":121,"color":6,"createdAt":394,"id":395,"name":396,"slug":396,"updatedAt":394},"2026-05-19T20:00:14.575Z","019e41d3-1aee-70c8-a07c-cec870115d14","pricing",{"articleCount":116,"color":6,"createdAt":398,"id":399,"name":400,"slug":400,"updatedAt":398},"2026-05-10T16:00:18.603Z","019e129e-34aa-723b-a568-45737915c7bf","qa",{"articleCount":116,"color":6,"createdAt":402,"id":403,"name":404,"slug":404,"updatedAt":402},"2026-05-08T04:00:18.013Z","019e05be-4c5c-75ad-8df5-602b3db57983","react",{"articleCount":5,"color":6,"createdAt":406,"id":91,"name":92,"slug":92,"updatedAt":406},"2026-04-20T12:00:11.653Z",{"articleCount":5,"color":6,"createdAt":408,"id":409,"name":410,"slug":410,"updatedAt":408},"2026-04-17T20:27:50.585Z","019d9d20-dfb9-7041-9973-39c545b33a2d","release",{"articleCount":121,"color":6,"createdAt":412,"id":413,"name":414,"slug":414,"updatedAt":412},"2026-05-26T08:00:14.619Z","019e634c-711a-728f-9b63-20f2c8b37ccb","routing",{"articleCount":121,"color":6,"createdAt":416,"id":417,"name":418,"slug":418,"updatedAt":416},"2026-05-19T16:00:13.509Z","019e40f7-5cc4-72e1-8f8b-692dd29fc716","sandbox",{"articleCount":116,"color":6,"createdAt":420,"id":421,"name":422,"slug":422,"updatedAt":420},"2026-05-14T12:00:21.362Z","019e265b-f571-7677-a537-2f7e96a490bf","scalability",{"articleCount":121,"color":6,"createdAt":424,"id":425,"name":426,"slug":426,"updatedAt":424},"2026-05-04T20:00:20.521Z","019df493-ce28-75e9-93d5-13251407a27b","scanning",{"articleCount":5,"color":6,"createdAt":428,"id":429,"name":430,"slug":430,"updatedAt":428},"2026-04-27T08:00:12.420Z","019dcdf3-fc84-73aa-a0be-6b7196c5a2e9","security",{"articleCount":116,"color":6,"createdAt":432,"id":433,"name":434,"slug":434,"updatedAt":432},"2026-06-17T12:00:12.428Z","019ed574-0a8b-7566-976f-8c281c820ed0","seo",{"articleCount":116,"color":6,"createdAt":436,"id":437,"name":438,"slug":438,"updatedAt":436},"2026-06-17T12:00:12.433Z","019ed574-0a91-74d8-b806-56681bb4b477","sitemap",{"articleCount":116,"color":6,"createdAt":440,"id":441,"name":442,"slug":442,"updatedAt":440},"2026-07-18T16:00:27.576Z","019f75f5-23b8-72eb-a2bf-79dd1fed3863","software-development",{"articleCount":116,"color":6,"createdAt":444,"id":445,"name":446,"slug":446,"updatedAt":444},"2026-04-17T19:35:19.297Z","019d9cf0-ca00-749d-9101-1c63bf62e215","spas",{"articleCount":163,"color":6,"createdAt":448,"id":449,"name":450,"slug":450,"updatedAt":448},"2026-06-03T16:00:12.620Z","019e8e36-bd4b-740d-b23a-81858b24025b","ssg",{"articleCount":452,"color":6,"createdAt":453,"id":454,"name":455,"slug":455,"updatedAt":453},9,"2026-04-08T06:47:43.020Z","019d6bd8-ca89-735e-a52a-ee53a80a77a9","ssr",{"articleCount":121,"color":6,"createdAt":457,"id":458,"name":459,"slug":459,"updatedAt":457},"2026-04-30T04:19:02.613Z","019ddc9c-9555-7544-a3e3-0605d2c1ea82","startup",{"articleCount":5,"color":6,"createdAt":461,"id":462,"name":463,"slug":463,"updatedAt":461},"2026-04-18T12:00:12.027Z","019da076-78fb-706b-9a4c-cee0c989cfff","state-management",{"articleCount":121,"color":6,"createdAt":465,"id":466,"name":467,"slug":467,"updatedAt":465},"2026-06-06T00:00:11.717Z","019e9a3a-e5c5-76d3-86e4-576c151a87b3","storage",{"articleCount":116,"color":6,"createdAt":469,"id":470,"name":471,"slug":471,"updatedAt":469},"2026-06-17T12:00:12.446Z","019ed574-0a9e-7712-8bc5-a0102787fe48","structured-data",{"articleCount":116,"color":6,"createdAt":473,"id":474,"name":475,"slug":475,"updatedAt":473},"2026-05-10T16:00:18.597Z","019e129e-34a4-771a-844d-09a7edefcd64","tdd",{"articleCount":121,"color":6,"createdAt":477,"id":478,"name":479,"slug":479,"updatedAt":477},"2026-06-04T20:00:17.351Z","019e9438-e5c6-7681-8704-897c7b499eb4","terms-of-service",{"articleCount":163,"color":6,"createdAt":481,"id":482,"name":483,"slug":483,"updatedAt":481},"2026-04-09T06:11:45.799Z","019d70de-3c07-76bf-9688-9619e1b0d427","testing",{"articleCount":116,"color":6,"createdAt":485,"id":486,"name":487,"slug":487,"updatedAt":485},"2026-06-16T16:11:31.088Z","019ed133-c390-75bf-8f1a-5c57cd221f14","threejs",{"articleCount":121,"color":6,"createdAt":489,"id":490,"name":491,"slug":491,"updatedAt":489},"2026-05-02T00:00:23.130Z","019de5fc-7e59-7426-8d46-e79e4bcf0d56","tls",{"articleCount":493,"color":6,"createdAt":494,"id":495,"name":496,"slug":496,"updatedAt":494},10,"2026-04-08T06:47:55.591Z","019d6bd8-fba5-743f-8f9f-4e23c0b31581","tutorial",{"articleCount":5,"color":6,"createdAt":498,"id":499,"name":500,"slug":500,"updatedAt":498},"2026-04-08T06:47:56.778Z","019d6bd9-0049-72ba-8cfa-139b1c1b249d","typescript",{"articleCount":502,"color":6,"createdAt":453,"id":503,"name":504,"slug":504,"updatedAt":453},5,"019d6bd8-ca8b-709c-b9a5-77d4910da162","ui-components",{"articleCount":116,"color":6,"createdAt":506,"id":113,"name":114,"slug":114,"updatedAt":506},"2026-05-11T12:00:19.969Z",{"articleCount":116,"color":6,"createdAt":508,"id":509,"name":510,"slug":510,"updatedAt":508},"2026-05-18T12:00:14.379Z","019e3af5-4a29-759a-81b0-b6d502b2449e","v-memo",{"articleCount":121,"color":6,"createdAt":512,"id":513,"name":514,"slug":514,"updatedAt":512},"2026-04-30T04:19:02.228Z","019ddc9c-93d3-763e-ad3c-d3ad78642de7","vercel",{"articleCount":116,"color":6,"createdAt":516,"id":517,"name":518,"slug":518,"updatedAt":516},"2026-07-13T08:00:18.274Z","019f5a7d-bf61-746b-a44b-5c0a16ff57d3","video",{"articleCount":5,"color":6,"createdAt":520,"id":521,"name":522,"slug":522,"updatedAt":520},"2026-04-17T19:35:19.289Z","019d9cf0-c9f8-7411-beed-02265d8271db","vite",{"articleCount":116,"color":6,"createdAt":524,"id":525,"name":526,"slug":526,"updatedAt":524},"2026-04-25T18:08:02.132Z","019dc5d3-c054-70e2-bca4-08e2a2b9a096","vitest",{"articleCount":116,"color":6,"createdAt":528,"id":529,"name":530,"slug":530,"updatedAt":528},"2026-06-27T12:00:13.107Z","019f08f3-a532-7049-a02c-c17a4fd99306","vscode",{"articleCount":532,"color":6,"createdAt":533,"id":32,"name":33,"slug":33,"updatedAt":533},32,"2026-04-08T06:47:42.793Z",{"articleCount":116,"color":6,"createdAt":535,"id":536,"name":537,"slug":537,"updatedAt":535},"2026-04-18T12:00:12.007Z","019da076-78e6-76bd-b0d4-4e0597d36378","vue-router",{"articleCount":121,"color":6,"createdAt":539,"id":540,"name":541,"slug":541,"updatedAt":539},"2026-05-04T20:00:20.510Z","019df493-ce1d-7039-811a-ed57bf081d26","vulnerability",{"articleCount":543,"color":6,"createdAt":544,"id":545,"name":546,"slug":546,"updatedAt":544},3,"2026-05-05T12:00:17.078Z","019df802-a8b6-74d2-a0cd-b0509cf502fa","web-development",{"articleCount":543,"color":6,"createdAt":548,"id":549,"name":550,"slug":550,"updatedAt":548},"2026-05-10T16:00:18.576Z","019e129e-3490-7739-a218-5454a8c15d6e","workflow"]