Templates Guide
What are Templates?
Templates in Sailor are TypeScript definitions that describe your content structure. Think of them as blueprints that tell the CMS:
- What content types you want to manage (posts, pages, products, etc.)
- What fields each content type should have (title, content, images, etc.)
- How the content should behave (SEO options, blocks support, URL structure)
How Templates Work
- Define: Write TypeScript templates describing your content structure
- Generate: Sailor automatically creates database tables, admin forms, and TypeScript types
- Use: Start creating and managing content through the admin interface
- Display: Use the generated utilities to fetch and display content in your frontend
Benefits
- No Database Setup: Sailor handles all the drizzle database schema generation
- Type Safety: Full TypeScript support with auto-generated types
- Admin UI: Beautiful, responsive admin interface automatically generated
- Flexible: Easy to modify and extend as your needs change
- Consistent: Standardized approach across all your content types
Template Types
Sailor supports three main template types:
- Collections: Content with multiple entries (blog posts, pages, products)
- Blocks: Reusable content components (hero sections, galleries, features)
- Globals: Site-wide content (settings, menus, configuration)
Common Template Structure
All templates share these core elements:
export const myTemplate = {
name: { singular: 'Item', plural: 'Items' },
slug: 'items',
description: 'Description of this content type',
icon: 'FileText', // Optional: sidebar icon
options: {
// Template-specific options
},
fields: {
// Your custom fields
}
};
Available Icons
Collections and globals automatically appear in the admin sidebar with these icons: FileText
, Layout
, FolderTree
, HelpCircle
, Menu
, Settings
, Image
, Users
, Calendar
, Tag
, Database
, Globe
, ShoppingCart
, BarChart
Core Fields
Collections & Repeatable Globals automatically include:
id
- UUID primary keytitle
- Display name (required)slug
- URL-friendly identifier (required, unique)-
status
- Published/draft status (enum: ‘draft’‘published’ ‘private’ ‘archived’) author
- Content creator (user ID, auto-populated, hidden by default)sort
- Manual ordering for drag-and-dropparent_id
- Parent item for hierarchical relationships (hidden by default)created_at
/updated_at
- Timestampslast_modified_by
- User who last modified the item (hidden by default)
Flat Globals (dataType: 'flat'
) only include:
id
- UUID primary keycreated_at
/updated_at
- Timestamps
Blocks include:
title
- Optional display namesort
- Manual ordering (required)
Collections
Content types with multiple entries (posts, pages, products).
Basic Collection
// src/lib/sailor/templates/collections/posts.ts
import type { CollectionDefinition } from '$sailor/core/types';
export const postsCollection: CollectionDefinition = {
name: { singular: 'Post', plural: 'Posts' },
slug: 'posts',
description: 'Blog posts with rich content',
icon: 'FileText',
options: {
titleField: 'title',
seo: true,
blocks: true,
basePath: '/blog/',
sortable: true
},
fields: {
excerpt: {
type: 'textarea',
label: 'Excerpt',
description: 'Short summary of the post'
},
content: {
type: 'richText',
label: 'Content'
},
featured_image: {
type: 'file',
label: 'Featured Image',
file: { fileType: 'image' }
},
tags: {
type: 'tags',
label: 'Tags'
}
}
};
Collection Options
Option | Type | Description |
---|---|---|
titleField | string | Field to display in admin lists and overviews |
seo | boolean | Adds SEO fields (meta_title, meta_description, og_title, og_description, og_image, canonical_url, noindex) |
blocks | boolean | Enable/disable blocks functionality (default: true ) |
basePath | string | Base URL path for preview links and SEO canonical URLs |
sortable | boolean | Enable drag-and-drop sorting on the collection table |
Registration
Register in src/lib/sailor/templates/collections/index.ts
:
export { postsCollection as posts } from './posts';
export { productsCollection as products } from './products';
Blocks
Reusable content components for flexible layouts.
Basic Block
// src/lib/sailor/templates/blocks/hero.ts
export const heroBlock = {
name: 'Hero Section',
slug: 'hero',
description: 'Large banner with title and background image',
options: {
titleField: 'title' // Field to display as title in block view
},
fields: {
title: {
type: 'string',
required: true,
label: 'Title'
},
subtitle: {
type: 'string',
label: 'Subtitle'
},
background_image: {
type: 'file',
label: 'Background Image',
file: { fileType: 'image' }
},
cta_buttons: {
type: 'array',
label: 'CTA Buttons',
items: {
type: 'object',
label: 'Button',
properties: {
text: { type: 'string', label: 'Button Text' },
url: { type: 'string', label: 'Button URL' },
style: {
type: 'select',
label: 'Button Style',
options: [
{ label: 'Primary', value: 'primary' },
{ label: 'Secondary', value: 'secondary' }
]
}
}
}
}
}
};
Block Scoping
Limit which block types are available for specific collections:
fields: {
layout: {
type: 'blocks',
label: 'Page Layout',
blocks: ['hero', 'richText', 'gallery'] // Only these block types allowed
}
}
Registration
Register in src/lib/sailor/templates/blocks/index.ts
:
export { heroBlock as hero } from './hero';
export { galleryBlock as gallery } from './gallery';
Globals
Site-wide settings and repeatable content.
Singleton Global
// src/lib/sailor/templates/globals/settings.ts
import type { GlobalDefinition } from '$sailor/core/types';
export const settingsGlobal: GlobalDefinition = {
name: { singular: 'Settings', plural: 'Settings' },
slug: 'settings',
description: 'Global site configuration',
icon: 'Settings',
options: {
singleton: true, // Only one entry allowed
titleField: 'site_name'
},
fields: {
site_name: {
type: 'string',
required: true,
label: 'Site Name'
},
tagline: {
type: 'string',
label: 'Tagline'
},
logo: {
type: 'file',
label: 'Site Logo',
file: { fileType: 'image' }
}
}
};
Repeatable Global
// src/lib/sailor/templates/globals/menus.ts
export const menusGlobal: GlobalDefinition = {
name: { singular: 'Menu', plural: 'Menus' },
slug: 'menus',
description: 'Navigation menus',
icon: 'Menu',
options: {
singleton: false, // Multiple entries allowed
sortable: true // Enable sorting for menu items
},
fields: {
name: { type: 'string', required: true, label: 'Menu Name' },
items: {
type: 'array',
label: 'Menu Items',
items: {
type: 'object',
properties: {
label: { type: 'string', label: 'Link Text' },
url: { type: 'string', label: 'URL' }
}
}
}
}
};
Registration
Register in src/lib/sailor/templates/globals/index.ts
:
export { settingsGlobal as settings } from './settings';
export { menusGlobal as menus } from './menus';
Common Field Types
Basic Fields
fields: {
// Text
title: { type: 'string', required: true, label: 'Title' },
description: { type: 'textarea', label: 'Description' },
content: { type: 'richText', label: 'Content' },
// Media
image: { type: 'file', label: 'Image', file: { fileType: 'image' } },
gallery: { type: 'array', label: 'Gallery', items: { type: 'file', file: { fileType: 'image' } } },
// Selection
category: { type: 'select', label: 'Category', options: ['News', 'Tutorial', 'Review'] },
tags: { type: 'tags', label: 'Tags' },
// Relationships
author: { type: 'relationship', label: 'Author', collection: 'users' },
// Layout
layout: { type: 'blocks', label: 'Page Layout' }
}
Field Options
Most fields support these common options:
required: boolean
- Make field mandatorylabel: string
- Display label in admindescription: string
- Help text below fieldhidden: boolean
- Hide field from admin interfacedefault: any
- Default value for new entries
Settings Configuration
Customize CMS behavior in templates/settings.ts
:
// src/lib/sailor/templates/settings.ts
import type { CMSSettings } from '$sailor/core/settings/types';
export const settings: Partial<CMSSettings> = {
storage: {
images: {
formats: ['webp', 'jpg', 'png'],
maxFileSize: '10.0MB',
maxWidth: 2560,
maxHeight: 2560,
defaultQuality: 85
},
upload: {
maxFileSize: '10.0MB',
allowedTypes: ['*/*'],
folderStructure: 'flat'
}
},
seo: {
enabled: true,
titleTemplate: '{title} | {siteName}',
titleSeparator: '|',
defaultDescription: 'Your site description',
language: 'en'
},
system: {
debugMode: false
}
};
Examples
E-commerce Product
export const productsCollection: CollectionDefinition = {
name: { singular: 'Product', plural: 'Products' },
slug: 'products',
description: 'E-commerce products',
icon: 'ShoppingCart',
options: {
titleField: 'name',
seo: true,
blocks: false,
basePath: '/shop/',
sortable: true
},
fields: {
name: { type: 'string', required: true, label: 'Product Name' },
price: { type: 'number', required: true, label: 'Price' },
description: { type: 'wysiwyg', label: 'Description' },
images: {
type: 'array',
label: 'Product Images',
items: { type: 'file', file: { fileType: 'image' } }
},
category: { type: 'select', label: 'Category', options: ['Electronics', 'Clothing', 'Books'] }
}
};
Simple Blog Post
export const postsCollection: CollectionDefinition = {
name: { singular: 'Post', plural: 'Posts' },
slug: 'posts',
description: 'Blog posts',
icon: 'FileText',
options: {
titleField: 'title',
seo: true,
blocks: true,
basePath: '/blog/',
sortable: false
},
fields: {
excerpt: { type: 'textarea', label: 'Excerpt' },
content: { type: 'richText', label: 'Content' },
featured_image: { type: 'file', label: 'Featured Image', file: { fileType: 'image' } },
tags: { type: 'tags', label: 'Tags' }
}
};
Note: Many settings like storage provider, S3 credentials, and database URL are configured via environment variables. See
environment-variables.md
for details.