Frontend Agent Playbook
Status
- document type: execution playbook for AI agents and contributors
- use this page when an agent needs to decide how to build, extend, or place frontend work inside
onelink - pair this with Frontend Implementation Guide for stack rules and Current Architecture for current runtime boundaries
Goal
Help an AI agent answer these questions consistently before writing frontend code:- where should this frontend change live?
- should the agent reuse, extend, or create a component?
- what internal source should it read first?
- what external library is allowed as an idea source or primitive source?
- when should it create a store, composable, or route-local component?
Read Order For Frontend Tasks
When the task touches dashboard, widget, portal, survey, or SDK frontend work, read in this order:AGENTS.md- Current Architecture
- Frontend Implementation Guide
- this playbook
- the closest existing screen or component in code
Step 1: Classify The Task
Before coding, classify the task into one of these buckets:- primitive component examples: button variant, dialog, tabs, dropdown, date input wrapper
- reusable feature component examples: card, sidebar section, table row, form block, appointment tile
- route or page examples: scheduling calendar page, integration settings page, contact details page
- overlay or embedded workflow examples: drawer, modal, conversation side panel, inline quick-create flow
- feature state and data layer examples: store, API client, search/filter composable
- domain overlay examples: healthcare-only section on top of a shared form or screen
Step 2: Search Internal Sources First
Use this search order before inventing new UI:app/javascript/dashboard/components-next/- stories under
components-next - similar route screens in
app/javascript/dashboard/routes/ - feature folders near the touched surface
- legacy
components/orcomponents/ui/only if the touched surface already depends on them
Step 3: Decide Reuse vs Extend vs Create
Use this matrix.Reuse Existing Component
Reuse when:- an existing component already matches the behavior and visual role
- only props, content, or slots need to change at call-site level
- introducing a second similar component would fragment the UI layer
- import the component
- pass different props, slots, or route data
Extend Existing Component
Extend when:- the component is already the right abstraction
- the new need is a variant, slot, event, or optional behavior that fits the same component role
- the change can be backward compatible for existing usages
- add prop, slot, emitted event, or variant class
- update Histoire story if the component is reusable
Create New Shared Component
Create a new shared component incomponents-next when:
- the missing element is likely reusable in multiple screens
- it represents a real UI primitive or stable feature block
- multiple routes would otherwise duplicate the same composition
- new folder under
components-next/<FeatureOrPrimitive>/ - local story in the same area if it is reusable or visually non-trivial
Create Route-Local Component
Create a route-local component when:- the component is tightly coupled to one screen or one workflow
- reusability is speculative rather than proven
- the component mainly composes existing shared primitives
- new component next to the route or page
- extract later only if reuse actually appears
Wrap Third-Party Primitive
Create a local wrapper when:- the project lacks a shared primitive like popover, tabs, combobox, or dialog behavior
- the need is reusable
reka-uior another approved primitive source solves the behavior cleanly
- local wrapper inside
components-next - external package hidden behind the local API
Step 4: Choose The Right Source For New UI
Use this sourcing order.Source 1: Existing Onelink UI
Best default source:components-next- Histoire stories
- existing route implementations
- API shape
- class naming
- slot patterns
- visual rhythm
- i18n patterns
- store and form patterns
Source 2: VueUse
Use for:
- composables and directives
- browser APIs and DOM observers
- outside-click, resize, scroll, media query, and storage helpers
- debounce, throttle, and lightweight query-param behavior
VueUse as the first external utility layer, not as a visual component system.
Source 3: reka-ui
Use for:
- accessible headless primitives
- interaction behavior
- keyboard and focus handling
- low-level overlay or selection patterns
reka-ui primitives directly throughout feature code. Wrap them locally first.
Source 4: originui-vue
Use for:
- copy-paste Vue component composition
- polished Tailwind app UI patterns
- input, settings, list, card, and control layouts that already fit an admin/dashboard feel
Source 5: shadcn-vue
Use for:
- example component composition
- prop and slot API ideas
- implementation recipes
- patterns for turning headless primitives into project-owned components
Source 6: Inspira UI
Use for:
- visual ideas
- animation or presentation ideas
- empty states, cards, hero-like sections, polished micro-layouts
Step 5: Place Files Correctly
Use this default structure for a new frontend feature:- API client:
dashboard/api - reusable store:
dashboard/stores - reusable logic:
dashboard/composables - shared UI:
dashboard/components-next - route screen composition:
dashboard/routes/dashboard/...
Step 6: Choose The Right State Pattern
No Store
Use no store when:- state is local to one component or one dialog
- there is no shared account-scoped data lifecycle
Composable
Use a composable when:- logic is reused across several components in the same feature
- you need derived state, side effects, keyboard behavior, or data formatting
Pinia
Use Pinia when:
- the feature has account-scoped entity data
- several screens or panels consume the same records or UI flags
- the feature needs CRUD, pagination, search, loading states, or caching
dashboard/store/storeFactory.js when the CRUD shape matches existing patterns.
Vuex Bridge
Use a Vuex bridge only when:
- the feature must read existing global boot/account/conversation state that still lives in
Vuex - migrating that state is out of scope
Vuex.
Step 7: Forms And Validation
Use this default form stack:components-nextinputs/selects/dialogs/textareaVuelidatefor validation- feature-local form state in
<script setup>
FormKit only when:
- the form is strongly schema-driven
- the integration already uses
FormKit - the form would otherwise require a large amount of repetitive dynamic rendering code
Step 8: Domain Overlay Rule
When a frontend feature has both shared and domain-specific parts:- build the shared platform screen first
- keep shared vocabulary, data flow, and layout generic
- add domain-specific sections, fields, or copy as a composition layer on top
Step 9: Verification Checklist
Before considering the frontend task complete, the agent should check:- the component or screen uses existing
components-nextpatterns where possible - strings are localized
- dark mode and token usage are not broken
- a reusable component has a Histoire story when appropriate
- non-trivial API client, store, or composable logic has targeted frontend tests when useful
- the real dashboard screen still boots and behaves correctly
Step 10: Minimum Deliverable Shape
For a new frontend feature, the minimum coherent deliverable should usually include:- API client
- state or composable layer
- route/page or embedded panel
- reused or newly wrapped shared components
- i18n wiring
- at least one verification path: Histoire, Vitest, or manual smoke
Agent Decision Tree
Use this quick rule set:- Is there already a near-match in
components-next? If yes: reuse or extend it. - Is the missing part behavior-only and already covered by
VueUse? If yes: useVueUsebefore adding a new helper package. - Is the missing part a reusable primitive?
If yes: wrap
reka-uior another approved primitive source incomponents-next. - Is the UI specific to one route? If yes: keep it route-local and compose existing shared parts.
- Does the feature need shared entity state?
If yes: use
Pinia. - Is the feature only adding domain-specific fields to a shared workflow? If yes: layer that on top of the shared screen, do not fork the whole module.
What The Agent Should Avoid
- adding
Nuxtor another application shell - importing third-party kits wholesale into production feature code
- building new reusable feature state in
Vuex - copying
originui-vue,shadcn-vue, orInspira UIcode without adapting it to local tokens and patterns - introducing a second design system under a new folder parallel to
components-next - inventing new abstractions before checking nearby route implementations
Still Missing
The repo is still missing two things that would make agent work even more deterministic:- a real code generator for new dashboard feature modules
- a dedicated design-token guide beyond the current Tailwind config and component code