vue-build-feature
Use when building, porting, or refactoring a Vue 3 feature that spans multiple components and layers: a page/view plus presentational components, composables, data access, a router route and shared utilities. Covers the view-orchestrates / components-present split, feature folders with a barrel (index.ts), data loading in composables, resetting shared state, router wiring and the build sequence. Triggers on building a new screen/view/feature, splitting a large `.vue` into smaller components, adding a data model/service, and wiring a router route. Single component's internals: `code-style`; Flux UI library: `flux-ui`.
How do I install this agent skill?
npx skills add https://github.com/basmilius/skills --skill vue-build-featureIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides architectural documentation and best practices for developing Vue 3 features. It contains no executable code, network operations, or security risks.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Vue build feature
How to build a Vue 3 feature that does not fit in one component: a page and the
components, composables, data access and route behind it, and how they wire
together across files. It is library-neutral. For how each individual .vue file
is built use the Vue reference of code-style; for picking UI
components (if the project uses Flux) use flux-ui; project-specific rules
(state library, permissions, i18n location) live in the repo's CLAUDE.md /
AGENTS.md.
1. Core principle: views orchestrate, components present
- The view (page) is the single orchestrator. It owns the data, state and
handlers by instantiating the feature composable (section 5). The state lives
in the composable; the view is the only place that wires it to children via
props /
v-model. Children never own canonical state. They emit or write a model to ask for a change. - Presentational components are props-in / events-out. They use
defineProps,defineEmitsanddefineModelonly, and fetch no data of their own. - Decompose, do not dump. Split each markup-heavy block (toolbar, filter, card, list, totals, empty state) into its own component once it stands alone.
2. The layers of one feature
A non-trivial feature usually touches these layers. Keep them separate:
| Layer | What it holds |
|---|---|
| Data access | Types/models and a service/client that talks to the API |
| Composable | Fetch + loading/error + derived state for the view |
| Components | Presentational pieces in a feature folder + a barrel |
| View / page | Orchestrator: owns state, composes components |
| Route | Router entry (lazy import, guards, meta) |
| Cross-cutting | Shared utils/formatters, icons, user-facing text (i18n) |
Where each layer lives differs per project; the separation is the constant. Never fetch inside a component; go through the data layer and a composable.
3. Build sequence (bottom-up)
Build from the layer with the fewest dependencies upward, so each layer compiles against the one below:
- Data access: types/models + the service method(s).
- Composable: wrap loading and state for the view.
- Presentational components: one per block, with a feature barrel.
- View: wire the composable to the components.
- Route: register the view.
- Cross-cutting: utils/formatters, icons, i18n keys.
4. Structure and barrels
- Group a feature's components in one folder, e.g.
component/<domain>/<feature>/, with atypes.tsfor shared shapes and anindex.tsbarrel that re-exports the folder. - Domain-shared components sit directly in
component/<domain>/, next to the feature sub-folders, not in acommon/sub-bucket. Keep feature-specific pieces in theircomponent/<domain>/<feature>/sub-folder, and put truly app-generic components in a top-levelcomponent/common/. - Import a domain-shared component by its own path
(
@/component/<domain>/<Name>.vue), not through the domain barrel: the barrel re-exports the feature that imports it, so routing through it creates an import cycle. - Import through the path alias (
@/component/...,@/composable, ...), never through deep relative paths. Use relative imports only for co-located children of the same feature. - Wire every new file into its barrel the moment you create it, so the rest of the app imports from one stable place.
component/
common/ app-generic components (AppIcon, layout, ...)
<domain>/
<Domain>Badge.vue shared directly across the domain's features
index.ts barrel: shared pieces + the feature barrels
<feature>/ one feature's presentational pieces
types.ts shapes shared within the feature
index.ts barrel: re-exports the folder
5. Load data in a composable, not in the view
- Put fetch + loading/error + debounce/race-guarding inside a
use<Feature>composable; the view consumes it ({ data, isLoading, isEmpty, error, reload }) and stays about composition, not plumbing. - Expose empty and error, not just loading. Derive
isEmptyso it cannot flash mid-fetch (loaded && !isLoading && count === 0) and render the states in order: skeleton while loading, then error, then empty, then data. - Do not hand-roll a
watch+fetch+ manual debounce in a view. Reach first for the project's existing data helper (a list/table or report composable, or a data-fetching library) and model a new one on the closest match. If none exists (greenfield), write one small race-guarded primitive (fetch + loading + error + a request token that drops stale responses) and model each feature composable on it. - If the project uses
@basmilius/common, itsuseDataTable/useDataReportare that data helper, backed by@basmilius/http-clientservices and DTOs (see thebasmiliusskill for both).
6. Reset shared / module-level state
- If a composable or store keeps its list in module-level (singleton) state
shared with child/overlay routes, give it a
reset()and call it in the list view'sonUnmounted(orwatcha context key and reset on change). Without it, revisiting the page re-renders stale, possibly heavy state synchronously before it reloads, which can freeze the UI with no spinner. - Component-scoped composable state needs no
reset(). State created inside a composable the view instantiates is torn down with the view; only module-level (singleton) or store state that outlives the view does. reset()must not trigger a refetch. If the composable refetches by watching its filter inputs, clearing them inreset()during teardown schedules a stale fetch; reset the inputs and cached data together without re-firing the fetch.
7. Router and routes
- One route per screen; lazy-load the view with a dynamic
import(). - Put cross-cutting concerns in route
meta(guards, permissions, title) and read them in a navigation guard, rather than checking inside each view. - Model modal/overlay screens as routes for deep-linking or a back action. Make
the overlay a child route of the list, rendered through a nested
<RouterView>, so the list stays mounted underneath. That is also why itsreset()fires only on leaving the feature (section 6). Pass route params as props (props: true), notuseRoute(), and close by navigating to the parent route, notrouter.back(), so a cold deep link still has an exit. - Permission/guard specifics are project-specific (follow the repo's
CLAUDE.md). The pattern itself holds everywhere: declare inmeta, enforce in a guard.
8. Cross-cutting
- Register icons in the project's central icon registry; type icon-name values
as the project's icon-name type, not
string. - Put shared formatters in
util/(barrel-exported), reusing the project's utility library instead of inliningIntl/formatting logic in templates. - Route user-facing text through the project's i18n layer, with keys for every locale it ships.
Related skills
code-style: how to build each.vuefile this feature adds.flux-ui: selecting and composing components when the project uses Flux.basmilius: if the project uses the@basmilius/*packages (by Bas Milius), the concrete data layer (DTO services) and the state/composable layer (useDataTable, stores) that this skill leaves project-specific.- The repo's
CLAUDE.md/AGENTS.md: the state/data library, permission model, i18n location and other project-specific rules.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/basmilius/skills/vue-build-feature">View vue-build-feature on skillZs</a>