Setup & config
Install one module, then shape it from two files: nuxt.config.ts for build-time, app.config.ts for everything the kit reads at runtime.
Source in the kit: module.ts · core.config.ts · locale/ · theme/
Starting from scratch? Skip steps 1–2
A Nuxt 3 project with @finema/core already installed and configured. Clone it and start building instead of setting everything up by hand.
1. Install
The module installs and configures @nuxt/ui, @pinia/nuxt and vee-validate for you.
bun add @finema/core
2. nuxt.config.ts
Add the module. Components, composables, helpers, v (valibot) and the vee-validate functions are then auto-imported everywhere.
export default defineNuxtConfig({
modules: ['@finema/core'],
// Where your API lives: read with useRuntimeConfig().public, overridable with NUXT_PUBLIC_* at runtime
runtimeConfig: {
public: {
baseAPI: 'https://api.example.com',
},
},
// Auto-import your loaders like the kit's own composables
imports: {
dirs: ['loaders'],
},
})3. app.config.ts
Everything under core is optional; these are the defaults. Date formats use date-fns tokens.
export default defineAppConfig({
core: {
site_name: '', // suffix of every page title
color: '#3675FB', // page loading bar
locale: 'th', // 'th' or 'en': kit texts, datepicker language
time_zone: 'Asia/Bangkok', // TimeHelper and date columns display in this zone
// What people see
date_format: 'dd-MMM-yyyy',
date_time_format: 'dd-MMM-yyyy HH:mm:ss',
month_format: 'MMM-yyyy',
time_format: 'HH:mm',
is_thai_year: false, // 2024 -> 2567
is_thai_month: false, // Feb -> ก.พ.
// What the API receives
date_format_system: 'yyyy-MM-dd',
date_time_format_system: 'yyyy-MM-dd HH:mm:ss',
// Loaders and tables
limit_per_page: 25,
default_primary_key: 'id',
},
})4. Changing the built-in texts
Dialog buttons, empty states, pagination and upload messages come from one dictionary. Override single entries; the rest keep the default for the locale. Keys are listed in src/runtime/locale/th.ts.
export default defineAppConfig({
core: {
messages: {
common: {
confirm: 'ยืนยัน',
empty: 'ยังไม่มีรายการ',
},
upload: {
maxSize: 'ไฟล์ใหญ่เกินไป (สูงสุด {size})', // {placeholders} are filled in by the kit
},
},
},
})5. Theming
The kit maps the Nuxt UI semantic colours to its own palettes (primary → main, defined in src/runtime/styles/main.css). Re-colour a palette in CSS, or point a semantic colour at another palette. Component classes are tailwind-variants slots under ui; kit components (table, form, dialog, uploadFileDropzone…) are themed the same way as Nuxt UI ones.
// assets/css/main.css changes the brand palette itself:
// @theme { --color-main-500: #7C3AED; --color-main-600: #6D28D9; /* …50 to 950 */ }
export default defineAppConfig({
ui: {
// or point a semantic colour at any Tailwind palette
colors: {
primary: 'violet',
},
// A Nuxt UI component
button: {
defaultVariants: {
size: 'lg',
},
},
// A kit component: slot names are in src/runtime/theme/table.ts
table: {
slots: {
th: 'bg-slate-50 text-slate-600',
},
},
},
})Reading the config in code
examples/config/Current.vueThe live state below is this playground's merged config.
Today in this app: 06 พ.ย. 2569
Confirm button text: ตกลง
Upload size error: ขนาดไฟล์ต้องไม่เกิน 5 MB
<template>
<div class="space-y-2 text-sm">
<p>
Today in this app:
<strong>{{ TimeHelper.displayDate(new Date()) }}</strong>
</p>
<p>
Confirm button text:
<strong>{{ messages.common.confirm }}</strong>
</p>
<p>
Upload size error:
<strong>{{ format(messages.upload.maxSize, { size: '5 MB' }) }}</strong>
</p>
</div>
</template>
<script lang="ts" setup>
// useCoreConfig() is the merged result of the kit defaults and the `core` key of your app.config.ts
const config = useCoreConfig()
// Every text the kit renders by itself, in core.locale, with core.messages overrides applied
const { messages, format } = useCoreMessages()
defineExpose({
debug: computed(() => config),
})
</script>