-
Notifications
You must be signed in to change notification settings - Fork 8
Developing Pages with TinaCMS
Note: This is documentation meant for developers when creating a new collection in TinaCMS. If you are a non-dev, you can create an issue to create a new page template.
- First, create a new file in the
.tina/collections
directory (can be a.ts
file or.tsx
file depending on if you plan to write inline JSX) - Now create the schema with the below template, replacing the placeholder values:
import type { Collection } from "tinacms";
import * as Schemas from "../../components/blocks";
import { seoSchema } from "../../components/util/seo";
export const {{ UNIQUE_NAME_HERE }}Schema: Collection = {
label: "{{ LABEL_HERE }}",
name: "{{ UNIQUE_NAME_HERE }}",
format: "mdx",
path: "content/{{ UNIQUE_NAME_HERE }}",
ui: {
router: ({ document }) => {
return `/{{ UNIQUE_NAME_HERE }}/${document._sys.filename}`;
},
},
fields: [
// @ts-expect-error no valid type for this schema yet
seoSchema,
{
type: "rich-text",
name: "_body",
label: "Body",
templates: [...Schemas.pageBlocks],
},
]
For more information on fields and field types, visit https://tina.io/docs/reference/fields/.
-
Then import the previously defined schema variable in the
.tina/config.tsx
file, and add it to the collections array. The order of the schema variables in this array is very important, as this is the order that the collections show up in the TinaCMS sidebar. -
After this, to ensure that our pages are receiving the correct data, we must create a custom GraphQL query. Add the following template to the
.tina/queries/queries.gql
file, replacing {{ UNIQUE_NAME_HERE }} with the value of the"name"
field in the schema object defined in step 2:
query {{ UNIQUE_NAME_HERE }}ContentQuery($relativePath: String!) {
...LayoutQueryFragment
{{ UNIQUE_NAME_HERE }}(relativePath: $relativePath) {
...{{ UNIQUE_NAME_HERE (except capitalise the first letter) }}Parts
}
}
- Now, create a
[filename].tsx
file in thepages
folder, aligning to what you want the parent path to be. For example, placing a[filename].tsx
in thepages/company
directory will result in a URL that aligns to/company/[slug]
. This slug value will be passed to the page component contained within[filename].tsx
. We want to use the GraphQL query we have just written to fetch data for the page in thegetStaticProps
function, which allows for static site generation (SSG). Here is a basic template for creating a new[filename].tsx
file (remembering to replace {{ UNIQUE_NAME_HERE }}):
import { InferGetStaticPropsType } from "next";
import { SEO } from "../../components/util/seo";
import { Container } from "../components/util/container";
import { Layout } from "../components/layout";
import { componentRenderer } from "../components/blocks/mdxComponentRenderer";
import { TinaMarkdown } from "tinacms/dist/rich-text";
import { client } from "../.tina/__generated__/client";
import { tinaField, useTina } from "tinacms/dist/react";
export default function Page(
props: InferGetStaticPropsType<typeof getStaticProps>
) {
const { data } = useTina({
data: props.data,
query: props.query,
variables: props.variables,
});
return (
<RecaptchaContext.Provider
value={{ recaptchaKey: props.env.GOOGLE_RECAPTCHA_SITE_KEY }}
>
<Layout>
<SEO seo={pageData.seo} />
<Container>
<TinaMarkdown
components={componentRenderer}
content={data.{{ UNIQUE_NAME_HERE }}._body}
/>
</Container>
<BuiltOnAzure data={{ backgroundColor: "lightgray" }} />
</Layout>
<SuccessToast />
</RecaptchaContext.Provider>
);
}
export const getStaticProps = async ({ params }) => {
const tinaProps = await client.queries.{{ UNIQUE_NAME_HERE }}ContentQuery({
relativePath: `${params.filename}.mdx`,
});
return {
props: {
data: tinaProps.data,
query: tinaProps.query,
variables: tinaProps.variables,
env: {
GOOGLE_RECAPTCHA_SITE_KEY:
process.env.GOOGLE_RECAPTCHA_SITE_KEY || null,
},
},
};
};
export const getStaticPaths = async () => {
const pagesListData = await client.queries.{{ UNIQUE_NAME_HERE }}Connection();
return {
paths: pagesListData.data.{{ UNIQUE_NAME_HERE }}Connection.edges.map((page) => ({
params: { filename: page.node._sys.filename },
})),
fallback: false,
};
};
This should allow you to be able to access your new route, feel free to play around with what other fields you can add to the collection apart from a body rich-text
.