Skip to content

Developing Pages with TinaCMS

Aman Kumar [SSW] edited this page Aug 15, 2023 · 8 revisions

Creating New Route Templates

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.

Workflow

  1. 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)
  2. 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/.

  1. 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.

  2. 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
  }
}
  1. Now, create a [filename].tsx file in the pages folder, aligning to what you want the parent path to be. For example, placing a [filename].tsx in the pages/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 the getStaticProps 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.