Loving Tina? us on GitHub0.0k

Docs

Learn

v.Latest
Documentation

Filename customization

Loading last updated info...
On This Page

By default, Tina does not enforce strict filename constraints, as operating systems support a wide variety of formats. However, you can enforce custom constraints at the collection level if needed.

Filename Rules

  • Filenames must contain only a-z, A-Z, 0-9 , -, _, ., or /.
  • Filenames cannot contain spaces
  • The filename generated must be unique within the collection
  • If the filename starts with /, it will be treated as an absolute path relative to the collection root
    • Example: /foo/bar/blog-post will be saved as <MyCollectionPath>/post/blog-post.md
  • If the filename does not start with /, it will be treated as relative to the current folder
    • Example: bar/blog-post will be saved as <MyCollectionPath>/<CurrentDirectory>/bar/blog-post.md

Configuration

Property

Description

ui.filename.readonly

Prevents the user from editing the filename

ui.filename.slugify

A function that generates the filename based on form values

ui.filename.parse

A function that sanitizes the filename input as the user types

Option A: Sanitizing Input with parse

The parse function allows you to enforce filename constraints within the editor. The provided callback function sanitizes user input in real-time.

Example using parse to enforce snake case

export default defineConfig({
//...
schema: {
collections: [
{
label: "Blog Posts",
ui: {
filename: {
parse: (filename) => filename.replaceAll(" ","_"),
}
},
name: "post",
path: "content/post",
format: "mdx",
fields: [
//...
]
}
],
},
});

Option B: Generating Filenames with slugify

Use the slugify function to automatically generate filenames based on other fields in the collection.

The slugify function only applies constraints when the user creates new files. Users can still rename files freely after creation.

Example with slugify and read only

export default defineConfig({
//...
schema: {
collections: [
{
label: 'Blog Posts',
name: 'post',
path: 'content/post',
format: 'md',
ui: {
filename: {
// if disabled, the editor can not edit the filename
readonly: true,
// Example of using a custom slugify function
slugify: (values) => {
// Values is an object containing all the values of the form. In this case it is {title?: string, topic?: string}
return `${values?.topic || 'no-topic'}-${values?.title
?.toLowerCase()
.replace(/ /g, '-')}`
},
},
},
fields: [
{
type: 'string',
label: 'Title',
name: 'title',
},
{
type: 'string',
label: 'Topic',
name: 'topic',
options: ['programming', 'blacksmithing'],
},
],
},
],
},
})

Example with default slugify

If no slugify function is provided and a field has isTitle: true, Tina uses a default slugify function. This removes non-alphanumeric characters and replaces spaces with dashes.

export default defineConfig({
//...
schema: {
collections: [
{
label: 'Blog Posts',
name: 'post',
path: 'content/post',
format: 'md',
fields: [
{
type: 'string',
label: 'Title',
name: 'title',
// If no slugify function is provided, then by default the "title" field will be used to generate the filename
isTitle: true,
required: true,
},
{
type: 'string',
label: 'Topic',
name: 'topic',
options: ['programming', 'blacksmithing'],
},
],
},
],
},
})
Last Edited: February 4, 2026