Formatting
Volt.Formatter is a mix format plugin — JS/TS files are formatted alongside Elixir using oxfmt via NIF (~30× faster than Prettier).
Add to .formatter.exs:
[
plugins: [Volt.Formatter],
inputs: [
"{mix,.formatter}.exs",
"{config,lib,test}/**/*.{ex,exs}",
"assets/**/*.{js,ts,jsx,tsx}"
]
]Or format manually:
mix volt.js.format
Configuration
config :volt, :format,
print_width: 100,
semi: false,
single_quote: true,
trailing_comma: :none,
arrow_parens: :alwaysAll oxfmt options are supported. Falls back to .oxfmtrc.json if no Elixir config is set.
Linting
Lint JS/TS assets using oxlint via NIF — 650+ rules, no Node.js required:
mix volt.lint
mix volt.lint --plugin react --plugin typescript
Available plugins: react, typescript, unicorn, import, jsdoc, jest, vitest, jsx_a11y, nextjs, react_perf, promise, node, vue, oxc.
Configuration
config :volt, :lint,
plugins: [:typescript],
rules: %{
"no-debugger" => :deny,
"eqeqeq" => :deny,
"typescript/no-explicit-any" => :warn
}Custom Rules
Custom lint rules can be written in Elixir using the OXC.Lint.Rule behaviour — see the oxc docs.
Combined Check
Check formatting and lint in one command (useful for CI):
mix volt.js.check
For TypeScript projects, run type-aware rules through tsgolint headless mode:
mix volt.js.check --type-aware
mix volt.js.check --type-aware --type-check
--type-aware also checks JavaScript-like scripts embedded in framework component files when the enabled plugin exposes them. Volt's built-in Vue and Svelte plugins expose <script> blocks as virtual .js, .ts, or .tsx modules for tsgolint, then map diagnostics back to the original .vue or .svelte file. Component templates are still handled by the normal syntax lint/format path; they are not passed to tsgolint.
Configure the executable when it is not on PATH:
config :volt, :lint,
tsgolint: "./node_modules/.bin/tsgolint",
rules: %{"typescript/no-floating-promises" => :deny}Exits with non-zero status on issues.