Home
Blog
Showcase
Community
Introduction
Overview
Introduction To TinaCMS
Getting Started
Using the Tina Editor
FAQ
Core Concepts
Content Modeling
Data Fetching
Visual Editing
Querying Content
Overview
Writing custom queries
Editing
Overview
Markdown & MDX
Block-based editing
Single Document Collections
Customizing Tina
Overview
Validation
Custom Field Components
Custom List Rendering
Format and Parse Input
Filename Customization
Before Submit function
Going To Production
Overview
Tina Cloud
Self-Hosted
Drafts
Overview
Draft Fields
Editorial Workflow
Guides
Overview
Framework Guides
Separate Content Repo
Querying Tina Content at Runtime
Internationalization
Migrating From Forestry
Reference
Overview
Config
Schema
The "tina" folder
The TinaCMS CLI
Media
Search
Content API
Tina's edit state
The "tinaField" helper
Self-Hosted Components

Markdown & MDX


Overview

Markdown (.md) and MDX (.mdx) are two popular formats for authoring content. Tina works great with both formats, and provides a simple WYSIWYG editor (so your editors won't have to manually write any markup).

What is markdown?

A basic .md might look like this:

---
title: This is my title
---
## Knock Knock!
Who's there?

The fields registered in between the --- delimiters, are called your frontmatter. Anything below is your markdown body.

What is MDX?

MDX is similar to Markdown, however it adds the ability to write JSX components in your markdown body.

---
title: This is my title
---
## Knock Knock!
Who's there?
<PunchLine text="orange your glad I didn't say banana?" />

Configuring the collection

To define a Tina collection that maps to a .md file, your schema might look like this:

const default defineConfig({
// ...
schema: {
collections: [
{
label: 'Blog Posts',
name: 'post',
path: 'content/posts',
format: 'md',
fields: [
{
type: 'string',
label: 'Title',
name: 'title',
},
{
type: 'rich-text',
label: 'Post Body',
name: 'body',
isBody: true,
},
],
},
]}
})
  • format, at the root of the collection, defines the filetype (md in this case).
  • All the frontmatter fields can be registered, and assigned a type.
  • Your markdown body is registered by defining a rich-text field, and setting isBody: true.

Rendering content with TinaMarkdown

When your markdown content is requested through Tina's API, Tina serves a parsed AST version of the content.

The parsed AST gives developers the ability to step through each node, and render it with full control.

Tina also provides a <TinaMarkdown> component, which renders your md or mdx component from the parsed AST.

Here's an example of fetching our markdown content, and rendering it on the page.

import { TinaMarkdown } from "tinacms/dist/rich-text";
// server code
const getStaticProps = () => {
const post = await client.queries.post({ relativePath: 'HelloWorld.md' })
return { data: { post: post } }
}
// Page component
const MyBlogPost = (props) => {
return (<>
<h1>{props.data.title}</h1>
<TinaMarkdown content={props.data.body} />
</>)
}

Providing "custom components" for MDX documents

If you are using mdx as the format, you'll have the ability to define custom components that your editors can leverage.

How does MDX work with Tina?Tina doesn't require a compilation step like other MDX tooling you might be familiar with, so it needs to know about all the possible elements you support ahead of time. Instead of doing an import statement in MDX, you need to register each element as a template in your Tina schema.

Defining a "template" in a collection

Tina needs to have each MDX component defined in advance, in the tina/config.{ts,js,tsx} file.

export default defineConfig({
// ...
schema: {
collections: [
{
label: 'Blog Posts',
name: 'post',
path: 'content/posts',
- format: 'md',
+ format: 'mdx',
fields: [
// ...
{
type: 'rich-text',
label: 'Post Body',
name: 'body',
isBody: true,
+ templates: [
+ {
+ name: "NewsletterSignup",
+ label: "Newsletter Sign Up",
+ fields: [
+ {
+ name: "children",
+ label: "CTA",
+ type: "rich-text",
+ },
+ {
+ name: "buttonText",
+ label: "Button Text",
+ type: "string",
+ }
+ ],
+ },
+ ],
},
],
},
],
}
})

By defining the above NewsletterSignup template, our editors now have the ability to add that template to the page body.

Saving a document would output a component in the markdown body that looks like this:

//...
<NewsletterSignup buttonText="Submit">### Hello world</NewsletterSignup>

Registering a "component" with TinaMarkdown

Once you've registered a template with a rich-text field in a collection, Tina still needs to know how to render the custom component.

Custom components can be defined with the components prop on <TinaMarkdown>.

const components = {
// The "NewsletterSignup" key maps to a "template" defined
// on our "rich-text" field
NewsletterSignup: (props) => {
return (
<>
<div>
<TinaMarkdown content={props.children} />
</div>
<div>
<form>
<label htmlFor="email-address">Email address</label>
<input name="email-address" type="email" required />
<button type="submit">{props.buttonText}</button>
</form>
</div>
</>
)
},
}
const MyBlogPost = (props) => {
return (
<>
<h1>{props.data.title}</h1>
<TinaMarkdown content={props.data.body} components={components} />
</>
)
}

Once our custom component has been registered with TinaMarkdown, editors can easily add components, and immediately see them rendered on the page.

Reference

For full usage details, check out the rich-text reference documentation.

Previous
Editing with Tina
Next
Block-based editing

Product

Showcase
Tina Cloud
Introduction
How Tina Works
Roadmap

Resources

Blog
Examples
Support
Media

Whats New
TinaCMS
TinaCloud
Use Cases
Agencies
Documentation
Teams
Jamstack CMS
Benefits
MDX
Markdown
Git
Editorial Workflow
Customization
SEO
Comparisons
TinaCMS vs Storyblok
TinaCMS vs Sanity
TinaCMS vs DecapCMS
TinaCMS vs Contentful
TinaCMS vs Builder.io
TinaCMS vs Strapi
Integrations
Astro
Hugo
NextJS
Jekyll