Loving Tina? us on GitHub0.0k
v.Latest
Documentation

Separate Content Repo

Loading last updated info...
On This Page

Introduction

Tina supports sourcing content from a separate Git repo. With this approach you can continue to define your Tina config in your "website repo", but documents will be saved externally in a “content repo”.

Why?

You might want to do this for a variety of reasons, such as:

  • To decouple your website code from your content files so your commit history isn't a mix of content updates and code updates.
  • To avoid having to give TinaCloud write access to your website's code (only give access to your content repo).

Throughout this guide we'll be referring to a "website repo" and a "content repo". The "website repo" is where your actual website is running, while the "content repo" is where you'll be storing your markdown content

Requirements

  • You must have both the "content repo" and the "website repo" checked out onto your local machine.
  • You must provide the location of your content repo in your Tina config (more about that below).

Create the website repository

We'll start by first creating the “website repo”. To help with this, we've created a basic template that you can clone down and follow along with. The template contains a NextJS site, and the tina config with the local config path already configured.

git clone https://github.com/tinacms/separate-website-repo.git

If you do not wish to start from a template, follow the quickstart guide on how to create a TinaCMS project. The important things you should have at the end of this step are as follow:

  1. A project that has TinaCMS setup within it.
  2. Said project is pushed to GitHub.
The project at this stage does not have to be setup and deployed within TinaCloud. This will be done at a later step within this guide.

Create the content repository

Next we'll setup the "content repo". Lets create a simple .mdx file to start with.

The below commands create a folder (demo-content-repo), initalise Git within said folder, and adds an initial MDX file to the content/pages directory. We'll be using that directory for the page collection defined in the Tina config.

tree .
# .
# └── separate-website-repo
#
# 2 directories, 0 files
mkdir -p demo-content-repo/content/pages
cd demo-content-repo
git init
mkdir tina
touch ./content/pages/home.mdx
echo "Hello" >> ./content/pages/home.mdx
cd ..
tree .
# .
# ├── demo-content-repo
# │ └── tina
# │   └── content
# │   └── pages
# │   └── home.mdx
# └── separate-website-repo
#
# 5 directories, 1 file

Before moving forward, ensure the “content repo” is pushed to GitHub.

Local Development

In the "website repo", install the project's dependencies and start the development server to generate the tina-lock.json and other required files

First, Navigate into the website repo

cd separate-website-repo

Then install the project dependencies

yarn install
yarn dev

Edit the content

With the development server running, open your browser of choice and head to http://localhost:3000/admin/index.html.

From here you can add more fields to you content models in tina/config.js. To learn more, visit the docs on content modeling.

Deploy the content repo in TinaCloud

With the content repo created locally and within GitHub, it's time to create it in TinaCloud. This process is detailed in the following guide.

Once that's done, take note of two tokens described below. You will need them for the next step.

  1. The client ID found on the projects "Overview" tab.
  2. The content token found on the projects “Tokens” tab.
You can confine content tokens to certain branches. To learn more, visit the docs on how to create a content token.

Deploy the website repo in TinaCloud

With the website repo created locally and within GitHub, it's time to create it in TinaCloud. This process is the exact same as done with the “content repo”.

  1. Create a .env file in the “website repo”
  2. Fill it out with the tokens retrieved in the previous step (from the content repo's TinaCloud instance)
cp .env.example .env

When you run tinacms build, it will use those credentials to connect to TinaCloud rather than the local server. If you're following along with the template, we've integrated this process into the yarn build command.

The final thing to do is take note of the client ID token found on the projects “Overview” tab. You will need it for the next step.

Connect the content repo

Now that the “website repo” has been created in TinaCloud:

  1. Grab the client ID retrieved in the previous step (from the website repo's TinaCloud instance)
  2. Create a new tina/meta.json file in the “content repo”. This is what connects the two together.
cd demo-content-repo
touch tina/meta.json

Then add the following contents into it:

{
"upstreamBranch": "main",
"upstreamClientId": "{{ website repo client ID }}"
}

Previous versions of TinaCMS required you to maintain an up-to-date /tina folder in both repositories. This has now been simplified to a single tina/meta.json file. If you're migrating from the way it used to be, ensure to update the repo in TinaCloud from the “content repo” to the “website repo”.

Path Configuration Considerations

When defining collection paths inside your tina/config.(ts/js), avoid setting path: '' . As Tina expects a folder-relative path. Using an empty string '' can result in errors when creating new documents because Tina prepends a / when saving, which can lead to invalid GitHub paths and creation errors.

Static Site Generator Compatibility

If you are using a static site generator (SSG) other than NextJS, such as Astro. Ensure your content pipeline ignores the tina/ directory inside your “content repo”. Tina's config files are not content files and this may cause parsing errors.

Here's an example of how to exclude the tina/ directory:

export default defineCollection({
schema: mySchema,
exclude: ['tina/**/*'],
});

Triggering Website Builds from the Content Repo (Optional)

When using a separate content repo, you may want content updates (e.g. from TinaCMS UI) to automatically trigger a build of your website. TinaCloud supports this out of the box using webhooks.

TinaCloud can be configured to send a POST request to a webhook endpoint whenever content changes are committed. This is the recommended way to trigger a rebuild of your website.

👉 Check out the webhook functionality for more details

Triggering Builds with GitHub Actions

As an alternative to using webhooks, you can trigger website builds with GitHub Actions.
This is useful if your platform doesn't support build webhooks, or if you want more control over the build process.

Example Workflow

Create a workflow file in your content repo (e.g. .github/workflows/trigger-website-build.yml):

name: Trigger Website Build
on:
workflow_dispatch:
push:
branches:
- '**'
jobs:
trigger-deploy:
runs-on: ubuntu-latest
if: github.repository_owner == 'your-org'
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.MY_APP_ID }}
private-key: ${{ secrets.MY_APP_PRIVATE_KEY }}
owner: your-org
repositories: your-website-repo
- name: Trigger website deploy
run: |
gh workflow run build-and-deploy.yml \
--repo your-org/your-website-repo \
--ref main \
--field run_id="$GITHUB_RUN_ID" \
--field branch_name="${GITHUB_REF#refs/heads/}"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
Prerequisites

This setup requires:

  • A GitHub App with access to both repos
  • Token and app secrets saved in GitHub repo settings
Last Edited: March 20, 2026