As much as we can all agree about how awesome markdown is, it will always be significantly more satisfying to see your edits and style changes on your pages in real time. 🦙 TinaCMS, when coupled with Gatsby, gives us to ability to manipulate our markdown files directly on our pages, visually. TinaCMS also works with Next.js, but here we’ll explore this powerful new tool with a Gatsby site.
We’ll get started with the gatsby-starter-blog starter, since that already has everything we need for markdown posts.
There are only four main things we need, TinaCMS itself, styled-components (which TinaCMS is dependent on), Tina’s remark
plugin for handling our markdown, and Tina’s git
plugin to give it the ability to directly alter files in our filesystem and make commits to your Github account when you save a post.
$ gatsby new tina-example https://github.com/gatsbyjs/gatsby-starter-blog
$ yarn add gatsby-plugin-tinacms styled-components gatsby-tinacms-remark gatsby-tinacms-git
The initial setup couldn’t be easier: we’ll add gatsby-plugin-tinacms
to gatsby-config.js
with the remark
and git
plugins. I personally prefer to set the position
to fixed
, since by default the sidebar will push our site to the side.
{
resolve: 'gatsby-plugin-tinacms',
options: {
plugins: [
"gatsby-tinacms-git",
"gatsby-tinacms-remark",
],
position: 'fixed'
}
}
With just that, your page should have a little blue tab on the bottom to open a new sidebar.
Now, in our post template we’ll need to get some extra things from our query; fileRelativePath
, rawFrontmatter
, rawMarkdownBody
. These will be used by the remarkForm
higher-order component that TinaCMS gives us, all we have to do is wrap our exported component with it and you should be good to go.
import { remarkForm } from "gatsby-tinacms-remark";
class BlogPostTemplate extends React.Component {
// ...
};
export default remarkForm(BlogPostTemplate);
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!) {
# ...
markdownRemark(fields: { slug: { eq: $slug } }) {
# ...
fileRelativePath
rawFrontmatter
rawMarkdownBody
}
}
`;
Now Tina has access to all your markdown posts and will update and quickly reload the page to show any new changes. Plus whenever you hit the save button there will be a small commit made to your Github repo, so any hosting platform connected to it will be updated automatically.
Having to work with long content entirely in the sidebar would be a pain, and instead we can setup our outputted content as the editor so we can directly interact with our content on the page and keep the sidebar just for metadata, theming, adding/deleting, etc.
Not much even needs to be changed, instead of remarkForm
we’ll wrap the page template in a liveRemarkForm
and wrap any part we want to be editable with a TinaField
. TinaField
just needs to be named rawMarkdownBody
and passed the Wysiwyg
library as a prop. Wysiwyg
is short for what you see is what you get, which is what will give us most of the real-time editing capabilities.
We’ll activate the editing mode whenever you click on the article itself, with the handy setIsEditing
method.
import { liveRemarkForm } from 'gatsby-tinacms-remark';
import { Wysiwyg } from '@tinacms/fields';
import { TinaField } from '@tinacms/form-builder';
const BlogPostTemplate = props => {
const { previous, next } = props.pageContext;
return (
<Layout>
{*/ ... */}
<TinaField name="rawMarkdownBody" Component={Wysiwyg}>
<article onClick={() => props.setIsEditing(true)}>
{*/ ... */}
</article>
</TinaField>
</Layout>
)
};
The first prop in the setIsEditing
method is the current editing state, so if instead you want to use a toggle button you can do something like props.setIsEditing(editing => !editing)
.
Removing files is ridiculously simple, just import the handy DeleteAction
method, throw into a an object with a label, and give it to liveRemarkForm
.
import { liveRemarkForm, DeleteAction } from 'gatsby-tinacms-remark';
const deleteButton = {
label: 'Delete',
actions: [DeleteAction]
};
export default liveRemarkForm(BlogPostTemplate, deleteButton);
Now next to the save button is a little menu with a Delete
option.
The ability to add posts is where things get a little heavy on the boilerplate.
We’ll use the createRemarkButton
function to add a new option to our sidebar. Whatever we return to filename
will be added to our filesystem, and we can use the slugify library to format the name for our file.
frontmatter
will handle the metadata for our post from our form. fields
will establish the form itself. Finally, we’ll use the withPlugin
HOC to add our new button to our layout.
import { withPlugin } from 'tinacms';
import { createRemarkButton } from 'gatsby-tinacms-remark';
import slugify from 'react-slugify';
const CreatePostButton = createRemarkButton({
label: "New Post",
filename(form) {
let slug = slugify(form.title.toLowerCase())
return `content/blog/${slug}/${slug}.md`
},
frontmatter(form) {
let slug = slugify(form.title.toLowerCase())
return new Promise(resolve => {
resolve({
title: form.title,
description: form.description,
data: new Date(),
path: `content/blog/${slug}/${slug}`,
})
})
},
fields: [
{ name: "title", label: "Title", component: "text", required: true },
{ name: "description", label: "Description", component: "text", required: true },
],
});
export default withPlugin(Layout, CreatePostButton);
There is still so much to explore with TinaCMS like creating/deleting pages with JSON and creating custom fields, blocks, and plugins.
Since TinaCMS a still fairly new tech, there’s still many ways it could be improved. They’re currently working on adding team support for different roles and editing permissions. Hopefully in the future they’ll add a way for a non-tech client to access Tina and edit everything on-site. 🤞
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!