nuxt-content-traits
v0.1.3
Published
Composable, feature-driven schema and configuration traits for Nuxt Content v3.
Downloads
716
Readme
Nuxt Content Traits
Features
- 🧩 Reusable Traits: Define schema fragments once and compose them into multiple collections by name.
- 🔒 Type-Safe: Full TypeScript inference for all trait fields — supports both Zod v4 and Valibot.
- ⚡ Runtime Access: Active traits and merged config are available at runtime via
useCollectionTraits. - 🔄 Per-Collection Overrides: Deep-merge global and per-collection trait config overrides via
app.config.ts.
Quick Setup
npx nuxt module add nuxt-content-traitsUsage
1. Define Traits and Collections
Replace defineContentConfig from @nuxt/content with the one from nuxt-content-traits/utils. Register shared traits in a traits registry and reference them by key in each collection.
// content.config.ts
import { z } from 'zod'
import { defineContentConfig, defineTrait } from 'nuxt-content-traits/utils'
export default defineContentConfig({
traits: {
dates: defineTrait({
schema: z.object({
date: z.string(),
dateEnd: z.string().optional(),
}),
}),
seo: defineTrait({
schema: z.object({
title: z.string().optional(),
description: z.string().optional(),
}),
}),
},
collections: {
article: {
type: 'page',
source: 'articles/**/*.md',
traits: ['dates', 'seo'],
},
},
})Valibot schemas are also supported — all traits within a collection must use the same validator.
import * as v from 'valibot'
import { defineContentConfig, defineTrait } from 'nuxt-content-traits/utils'
export default defineContentConfig({
traits: {
dates: defineTrait({
schema: v.object({
date: v.string(),
dateEnd: v.optional(v.string()),
}),
}),
},
collections: {
post: {
type: 'page',
source: 'posts/**/*.md',
traits: ['dates'],
},
},
})2. Add Trait Config (Optional)
Trait config lives in app.config.ts. You can define global values and per-collection overrides — they are deep-merged and consumed at runtime via useCollectionTraits.
// app.config.ts
export default defineAppConfig({
content: {
traits: {
// Global config per trait, available to all collections
dates: { format: 'long' },
},
collections: {
article: {
// Per-collection overrides per trait, takes precedence over global
dates: { format: 'short' },
},
},
},
})3. Consume at Runtime
Use useCollectionTraits to access the active traits and merged config from app.config.ts in your components.
<script setup lang="ts">
const { data: article } = await useAsyncData('article', () =>
queryCollection('article').first()
)
const { activeTraits, hasTrait, traitConfig } = useCollectionTraits('article')
// traitConfig.dates.format === 'short' (merged from app.config.ts)
</script>
<template>
<div>
<time v-if="hasTrait('dates') && article?.date">
{{ article.date }}
</time>
</div>
</template>Contribution
# Install dependencies
pnpm install
# Generate type stubs
pnpm run dev:prepare
# Develop with the playground
pnpm run dev
# Build the playground
pnpm run dev:build
# Run ESLint
pnpm run lint
# Run Vitest
pnpm run test
pnpm run test:watch
# Release new version
pnpm run release