The Music Assistant frontend/panel is developed in Vue, development instructions below.
VSCode + Volar (and disable Vetur) + TypeScript Vue Plugin (Volar).
TypeScript cannot handle type information for .vue imports by default, so we replace the tsc CLI with vue-tsc for type checking. In editors, we need TypeScript Vue Plugin (Volar) to make the TypeScript language service aware of .vue types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a Take Over Mode that is more performant. You can enable it by the following steps:
- Disable the built-in TypeScript Extension
- Run
Extensions: Show Built-in Extensionsfrom VSCode's command palette - Find
TypeScript and JavaScript Language Features, right click and selectDisable (Workspace)
- Run
- Reload the VSCode window by running
Developer: Reload Windowfrom the command palette.
See Vite Configuration Reference.
nvm use node
yarn installyarn devThis will launch an auto-reload development environment (usually at http://localhost:3000) Open the url in the browser and a popup will ask the location of the MA server. You can either connect to a locally launched dev server or an existing running server on port 8095.
yarn buildLint with ESLint
yarn lintThis project is migrating from Vuetify to shadcn-vue as its primary UI component library.
- Size limit: Keep components under 300–400 lines. If a component grows beyond this, split it into smaller, focused sub-components.
- Single responsibility: Each component should do one thing well. Extract repeated logic or UI patterns into reusable components.
- Composition over complexity: Prefer composing small components rather than building monolithic ones with many responsibilities.
- Use shadcn-vue: All new UI should use shadcn-vue components located in
src/components/ui/. Do not introduce new Vuetify components. - Extend, don't override: If a shadcn-vue component needs customization, extend it via props or slots rather than overriding styles globally.
- Avoid inline styles: Use Tailwind utility classes for styling. Avoid
style=""attributes except for dynamic values that cannot be expressed as classes.
- Always type props and emits: Define explicit types for all component props and emits — avoid
any. - Prefer
interfacefor object shapes: Useinterfacefor defining data shapes andtypefor unions/intersections. - No implicit
any: Every function parameter and return value should be typed or clearly inferrable.
- Extract reusable logic into composables: Any stateful logic shared between two or more components belongs in a
composableundersrc/composables/. - Keep
<script setup>lean: Heavy logic (data fetching, transformations) should live in composables, not inline in the component.
- Pure functions go in
src/helpers/: Any standalone utility function (e.g. string manipulation, date formatting, data transformation) must be placed insrc/helpers/rather than inlined in a component or composable. - One file per concern: Group related helpers in a named file (e.g.
src/helpers/string.ts,src/helpers/date.ts). Avoid a single catch-allutils.ts. - Test every helper: Each helper file must be covered by unit tests. For new helpers, add a corresponding test file colocated with the helper (e.g.
src/helpers/string.test.ts). Existing tests undertests/helpers/may remain, but new tests should follow the colocated pattern. Helpers with no test coverage should not be merged.
- Always show feedback on API calls: Every API call must be wrapped with user feedback:
- On success: call
toast.success(...)with a clear confirmation message. - On failure: call
toast.error(...)with a meaningful error message — never silently swallow errors.
- On success: call
- Example pattern:
try { await api.doSomething() toast.success("Action completed successfully") } catch (e) { toast.error("Failed to complete action") }
- Do not use
console.erroras a substitute for user-facing feedback on API errors (or for meaningful calls).
- No magic numbers/strings: Extract constants with descriptive names.
- Meaningful naming: Variables, functions, and components should clearly describe their purpose. Avoid abbreviations unless universally understood.
- Keep templates readable: If a template expression is complex, move it to a computed property.
- Clean up side effects: Always clean up event listeners and intervals in
onUnmounted; manually created watchers outside componentsetupor manual effect scopes must also be cleaned up. - Accessibility: Use semantic HTML elements and provide
aria-*attributes where appropriate.
We use Lokalise to manage the translation files for the Music Assistant frontend
If you wish to assist in translating Music Assistant into a language that it currently does not support, please see here https://music-assistant.io/help/lokalise/.

