> ## Documentation Index
> Fetch the complete documentation index at: https://docs.seosorted.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub

> Publish articles as Markdown, MDX or HTML files in your site's GitHub repository. Your host deploys them like any other change.

If your site is built from code (Astro, Next.js, Hugo, Eleventy, Jekyll, or plain HTML) and
deploys from GitHub through Cloudflare Pages, Vercel, Netlify or GitHub Pages, you don't need a
CMS. The GitHub connector writes each finished article as a file in your repository. Your host
sees the commit and deploys it, the same as when your team pushes a change.

You can choose how changes land:

* **Commit straight to the branch.** The article goes live on the next deploy.
* **Open a pull request for each article.** Someone on your team reviews and merges it, then it
  deploys.

> [!NOTE]
>
> You sign in with GitHub. There is no token to create or paste. Access goes through Composio,
> which holds the GitHub authorization for us. You can revoke it at any time from GitHub
> (**Settings → Applications → Authorized OAuth Apps**) or by disconnecting in SEOsorted.

## Before you start

* The GitHub account you connect needs **write access** to the repository. Only repositories it
  can push to are listed.
* For a repository owned by a **GitHub organization**, the organization may need to approve the
  app first. If the repository doesn't appear, an organization owner can approve it under
  **Organization settings → Third-party access → OAuth app policy**.
* Know which **folder** your site reads blog posts from (for example `src/content/blog` in Astro,
  `content/posts` in Hugo, `_posts` in Jekyll) and which **branch** your host deploys.

## Step 1: Connect GitHub

1. In SEOsorted, open **Settings → Connections** and choose **GitHub**.
2. Sign in to GitHub if asked, then click **Authorize**.
3. You're brought back to SEOsorted, where GitHub shows **Setup required — pick a repository**.

## Step 2: Choose where articles go

Click **Finish setup** and fill in:

| Setting | What it's for |
| - | - |
| **Repository** | The site's repository. |
| **Branch** | The branch your host deploys from, usually `main`. |
| **Folder for articles** | Where article files are written, for example `src/content/blog`. Leave it empty for the repository root. |
| **File format** | **Markdown (.md)** or **MDX (.mdx)** with frontmatter, or a standalone **HTML page (.html)**. |
| **How to publish** | **Commit straight to the branch**, or **Open a pull request for each article**. |
| **Site URL** | Used to build each article's live URL. Leave it empty to use your workspace website. |

Click **Save**. We check that the account can push to the repository and that the branch
exists before saving.

Each article becomes one file named after its slug, for example
`src/content/blog/how-to-choose-a-crm.md`. Its live URL is your **Site URL**, plus your blog path
(**Settings → Website**, `/blog` by default), plus the slug.

## What the file contains

### Markdown and MDX

The article body in Markdown, with this frontmatter:

```yaml theme={null}
---
title: "How to choose a CRM"
description: "The meta description"
slug: "how-to-choose-a-crm"
date: "2026-11-04T09:30:00.000Z"
author: "Your Brand"
image: "https://…/featured.png"
imageAlt: "How to choose a CRM"
canonical: "https://example.com/blog/how-to-choose-a-crm"
tags:
  - "crm"
  - "sales software"
readingTime: 7
---
```

`metaTitle` is added when the SEO title differs from the headline. Fields with no value are left
out. If your content collection uses different names (for example `pubDate` instead of `date`),
map them in your site's schema or layout. The values are always written under these names.

The **MDX** format escapes `{` and `}` and self-closes `<br>`, `<hr>` and `<img>` tags, so the file
compiles as MDX.

### HTML

A complete page: `<title>`, meta description, canonical link, Open Graph tags, the article's JSON-LD
schema, then the article inside `<article>`. It has no site header, footer or styles. Use it when
your site serves HTML files as they are, or when a build step wraps them in your layout.

### Images

Images stay on SEOsorted's image host and are referenced by URL. Nothing is added to your
repository except the article file.

## Re-publishing, unpublishing and deleting

| Action | What happens in the repository |
| - | - |
| **Re-publish** | The same file is updated in place. If the slug changed, the new file is written and the old one removed. |
| **Unpublish** | The file is deleted. |
| **Delete article** | The file is deleted. |

In **pull request** mode, every change goes through a branch named `seosorted/<slug>` (or
`seosorted/remove-<slug>` for deletions) and a pull request into your branch. Re-publishing
while that pull request is still open adds a commit to it, so you don't get a second pull request.

## Tracking AI crawlers

Track AI crawlers on the site built from your repository. For GitHub Pages or S3, put the domain behind Cloudflare: follow [Cloudflare (any site)](https://docs.seosorted.ai/connectors/ai-crawler-tracking.md#cloudflare-any-site). For Netlify, Next.js and other hosts and frameworks, pick your option in [Which option to pick](https://docs.seosorted.ai/connectors/ai-crawler-tracking.md#by-framework-and-host).

## Troubleshooting

> [accordion] My repository isn't in the list
>
> The connected account can't push to it. Ask for write access, or reconnect with an account that
> has it. For organization repositories, the organization may need to approve the app (see
> **Before you start**).

> [accordion] 'protected branch' error when publishing
>
> Your branch requires pull requests or status checks, so commits can't be pushed to it directly.
> Open **Configure** on the GitHub connection and switch **How to publish** to **Open a pull
> request for each article**.

> [accordion] Published, but the live URL is a 404
>
> * In pull request mode, the pull request isn't merged yet.
> * Your host hasn't finished deploying. Check its deploy log for the commit.
> * The folder or URL doesn't match how your site routes pages. Check that **Folder for articles**
>   is the folder your site builds posts from, and that your blog path (**Settings → Website**)
>   matches the URL your site serves them at.

> [accordion] The page builds, but fields are missing
>
> Your layout reads frontmatter fields under different names. Map our field names (see **What the
> file contains**) in your content schema or layout.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.