Project Structure
Overview
A Foundry project has two main layers: your app (what you customize) and the Foundry layer (what you extend). You edit your app; Foundry provides the framework.
my-project/
├── app/
│ ├── app.config.ts # App title, collections, routing, logo
│ ├── assets/
│ │ └── theme.css # Brand colors (oklch)
│ └── components/
│ └── section/ # Override landing page sections
│ └── Hero.vue # Custom hero (overrides layer default)
│
├── content/
│ ├── config/
│ │ ├── navigation.yml # Header, footer, banner links
│ │ └── site.yml # Business name, socials, mission
│ ├── faq/ # FAQ entries (YAML)
│ ├── pages/
│ │ ├── index.md # Landing page
│ │ ├── about.md # About page
│ │ ├── offers/ # Offer detail pages
│ │ └── success/ # Post-conversion pages
│ └── team/
│ └── founder.yml # Team member profiles
│
├── server/
│ └── plugins/
│ └── evlog-drain.ts # Log drain (Sentry/Axiom/PostHog)
│
├── nuxt.config.ts # Extends layer, route rules, modules
├── content.config.ts # Content collection definitions
├── .env # Environment variables
└── public/ # Favicons and static assets
Your App vs The Layer
What You Own
Everything in your project directory is yours to customize:
content/— All page content, configuration, and data. This is where you spend most of your time.app/components/— Override any layer component by creating a file with the same name and path.app/assets/theme.css— Your brand colors.app/app.config.ts— How collections map to routes, what's searchable, logo paths.nuxt.config.ts— Route rules, module configuration, site metadata.content.config.ts— Which content collections exist and what schemas they use.server/plugins/— Your log drain and any server-side customizations.
What the Layer Provides
The @incubrain/foundry layer gives you everything else:
- 4 layouts —
landing,default,article,docs - 33+ components — signal capture forms, section wrappers, navigation, content display
- 8+ composables — event tracking, content fetching, navigation, storage
- 7 modules — config, CSS, events, RSS, changelog, docs, comments
- Server handlers — webhook delivery, RSS feeds, MCP tools
- Content schemas — Zod schemas for all 9 collection types
You never need to edit the layer directly. Override what you need from your app.
Key Files Explained
content.config.ts
Defines your content collections and their schemas. Each collection maps a directory of YAML or Markdown files to a typed data source.
import { defineContentConfig, defineCollection } from '@nuxt/content'
import { basePageSchema, baseFaqSchema } from '@incubrain/foundry/schemas'
export default defineContentConfig({
collections: {
pages: defineCollection({
type: 'page', // Routable — generates URL paths
source: {
include: 'pages/**/*.md',
prefix: '/',
},
schema: basePageSchema,
}),
faq: defineCollection({
type: 'data', // Data-only — queried but not routed
source: {
include: 'faq/*.yml',
},
schema: baseFaqSchema,
}),
},
})
The schemas are imported from @incubrain/foundry/schemas — this ensures your content files match the shape the layer components expect. See the Content Schemas Reference for all available schemas.
nuxt.config.ts
Your Nuxt configuration. The critical parts are:
export default defineNuxtConfig({
// Extend the Foundry layer
extends: ['@incubrain/foundry'],
// Add optional modules
modules: ['nuxt-studio', 'nuxt-llms'],
// Assign layouts to routes
routeRules: {
'/': { appLayout: 'landing' },
'/about': { appLayout: 'default' },
'/offers/**': { appLayout: 'article' },
'/success': { appLayout: 'landing' },
'/success/**': { appLayout: 'landing' },
},
})
The routeRules block is how you assign layouts to URL patterns. Every page needs a layout.
app/app.config.ts
Runtime configuration that controls how the layer behaves. The most important section is content.collections which maps collection keys to their names and route prefixes:
export default defineAppConfig({
title: 'My Product',
content: {
collections: {
pages: { name: 'pages', type: 'page', prefix: '/' },
faq: { name: 'faq', type: 'data' },
config: { name: 'config', type: 'data' },
navigation: { name: 'navigation', type: 'data' },
searchable: ['pages'],
},
defaultAuthor: 'founder',
routing: {
offers: '/offers',
success: '/success',
},
},
})
See Configuration for the full shape.
How Nuxt Layers Work
Foundry is distributed as a Nuxt Layer, which means your project files always take priority over the layer's defaults. You can override any component, layout, or composable by creating a file with the same name and path in your app.
For details on how Nuxt Layers work, see the Nuxt Layers documentation.
The Content Directory
Content is the heart of a Foundry project. Here's what each subdirectory does:
| Directory | Format | Purpose |
|---|---|---|
content/config/ | YAML | Site-wide configuration (business info, navigation) |
content/pages/ | Markdown | Routable pages (landing, about, offers, success) |
content/faq/ | YAML | FAQ entries grouped by type |
content/team/ | YAML | Team member profiles |
content/decisions/ | Markdown | Optional changelog/decision log |
content/docs/ | Markdown | Optional documentation pages |
All content files are validated against Zod schemas at build time. If a file doesn't match its schema, you'll get a clear error message.
Docs Directory Structure
If you're building documentation, each docs subdirectory requires a navigation.yml file to define its title and icon in the sidebar:
content/docs/
├── 1.getting-started/
│ ├── navigation.yml # Required for sidebar display
│ ├── 1.introduction.md
│ ├── 2.quickstart.md
│ └── 3.configuration.md
└── 2.content/
├── navigation.yml # Required for sidebar display
├── 1.overview.md
└── 2.pages.md
Each navigation.yml follows this structure:
title: 'Getting Started'
icon: i-lucide-rocket
Important: Do NOT create index.md files in docs subdirectories — they conflict with navigation.yml. Use numbered files instead (1.introduction.md, 2.quickstart.md, etc.). When navigating to a directory path like /docs/getting-started, the system automatically redirects to the first child page.
Next Steps
Learn how to configure everything in Configuration.