← Back to list

Building Dynamic Release Notes with GitHub API: A Complete React Implementation

When building documentation sites or project dashboards, displaying release notes is crucial for keeping users informed about updates and…

Giovambattista Fazioli · 2025-07-01 12:30 · 3 claps · 4.3 min read paywalled
#react-js-tutorials #nextjs-tutorial #github #typescript-tutorial #mantine-ui
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🔓 · Open Source 🎬 · Film & Television

Building Dynamic Release Notes with GitHub API: A Complete React Implementation

GitHub Release Notes

GitHub Release Notes

When building documentation sites or project dashboards, displaying release notes is crucial for keeping users informed about updates and changes. Instead of manually maintaining a changelog, we can leverage GitHub’s API to automatically fetch and display release information in a beautiful, interactive format.

In this article, we’ll explore how to build a complete release notes system using React, TypeScript, and GitHub’s API, featuring automatic MDX compilation, table of contents generation, and elegant UI components.

Why GitHub API for Release Notes?

Using GitHub’s releases API offers several advantages:

  • Automatic synchronization with your actual releases
  • Markdown support for rich formatting
  • No manual maintenance required
  • Version control integration
  • Free and reliable API access

Project Structure

Our implementation consists of three main components:

components/ReleaseNotes/
├── use-release-notes.ts    # Custom hook for data fetching
├── release-notes-toc.ts    # Table of contents generator
└── ReleaseNotes.tsx        # Main UI component

app/api/github-releases/
└── route.ts               # API endpoint for server-side fetching

config/
└── index.ts              # Configuration settings

Configuration Setup

First, let’s set up our configuration to centralize GitHub API settings:

// config/index.ts
export default {
  gitHub: {
    repo: 'your-username/your-repo',
    apiUrl: 'https://api.github.com',
    releasesUrl: 'https://api.github.com/repos/your-username/your-repo/releases',
  },
  releaseNotes: {
    url: 'https://github.com/your-username/your-repo/releases',
    maxReleases: 10,
  },
} as const;

API Route Implementation

Create a Next.js API route to handle GitHub API requests server-side:

// app/api/github-releases/route.ts
import config from '@/config';

export async function GET(request: Request) {
  try {
    const response = await fetch(
      `${config.gitHub.releasesUrl}?per_page=${config.releaseNotes.maxReleases}`,
      {
        headers: {
          Accept: 'application/vnd.github+json',
          // Optional: Add GitHub token for higher rate limits
          // Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
        },
      }
    );

    if (!response.ok) {
      return Response.json(
        { error: 'Failed to fetch releases' }, 
        { status: response.status }
      );
    }

    const releases = await response.json();
    return Response.json({ releases, status: 'ok' });
  } catch (error) {
    return Response.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

TypeScript Interfaces

Define clean TypeScript interfaces for type safety:

// types/release.ts
export interface Release {
  id: number;
  tag_name: string;
  name: string;
  body: string;
  created_at: string;
  published_at: string;
  html_url: string;
  draft: boolean;
  prerelease: boolean;
}

export interface Author {
  login: string;
  avatar_url: string;
  html_url: string;
}

Custom Hook for Data Fetching

Create a custom hook that fetches and processes release data:

// components/ReleaseNotes/use-release-notes.ts
import { useEffect, useState } from 'react';
import { compileMdx } from 'nextra/compile';
import useSWR from 'swr';

export function useReleaseNotes() {
  const [compiledReleases, setCompiledReleases] = useState<Release[]>([]);

  const { data, error, isLoading } = useSWR(
    '/api/github-releases',
    (url) => fetch(url).then((res) => res.json())
  );

  useEffect(() => {
    if (data?.releases && !isLoading) {
      const processReleases = async () => {
        const processed = await Promise.all(
          data.releases.map(async (release) => ({
            ...release,
            // Format date for display
            created_at: new Date(release.created_at).toLocaleDateString('en-US', {
              year: 'numeric',
              month: 'long',
              day: 'numeric',
            }),
            // Compile markdown content
            body: await compileMdx(release.body),
          }))
        );
        setCompiledReleases(processed);
      };

      processReleases();
    }
  }, [data, isLoading]);

  return { 
    data: compiledReleases, 
    error, 
    isLoading 
  };
}

Table of Contents Generator

For documentation integration, create a TOC generator:

// components/ReleaseNotes/release-notes-toc.ts
import config from '@/config';

export async function releaseNotesToc() {
  try {
    const response = await fetch(
      `${config.gitHub.releasesUrl}?per_page=${config.releaseNotes.maxReleases}`
    );

    const releases = await response.json();

    return releases.map((release) => ({
      value: `${release.tag_name} - ${new Date(release.created_at)
        .toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}`,
      depth: '3',
      id: release.tag_name,
    }));
  } catch (error) {
    console.error('Error generating TOC:', error);
    return [];
  }
}

React Component Implementation

Finally, create the main UI component using Mantine for beautiful styling:

// components/ReleaseNotes/ReleaseNotes.tsx
'use client';

import { IconBrandGithub } from '@tabler/icons-react';
import { MDXRemote } from 'nextra/mdx-remote';
import { 
  Alert, 
  Badge, 
  Button, 
  Group, 
  Loader, 
  Paper, 
  Stack, 
  Text 
} from '@mantine/core';
import config from '@/config';
import { useReleaseNotes } from './use-release-notes';

export function ReleaseNotes() {
  const { data, error, isLoading } = useReleaseNotes();

  if (error) {
    return (
      <Alert color="red" title="Failed to load releases">
        {error.message || 'An error occurred while fetching releases'}
      </Alert>
    );
  }

  if (isLoading) {
    return (
      <Stack align="center" mt={24}>
        <Group>
          <Text>Loading releases...</Text>
          <Loader type="dots" />
        </Group>
      </Stack>
    );
  }

  return (
    <Stack mt={24}>
      {data.map((release) => (
        <Paper
          key={release.id}
          id={release.tag_name}
          withBorder
          p={24}
          radius={12}
          shadow="sm"
        >
          <Group justify="space-between" mb="md">
            <Badge size="xl" variant="filled">
              {release.tag_name}
            </Badge>
            <Text c="dimmed">{release.created_at}</Text>
          </Group>

          <MDXRemote 
            compiledSource={release.body}
            components={{
              // Custom MDX components can be added here
            }}
          />
        </Paper>
      ))}

      <Button
        component="a"
        href={config.releaseNotes.url}
        variant="gradient"
        leftSection={<IconBrandGithub size={18} />}
        target="_blank"
      >
        View full changelog on GitHub
      </Button>
    </Stack>
  );
}

Usage Example

Using the component is incredibly simple:

// pages/changelog.tsx
import { ReleaseNotes } from '@/components/ReleaseNotes/ReleaseNotes';

export default function ChangelogPage() {
  return (
    <div>
      <h1>Release Notes</h1>
      <ReleaseNotes />
    </div>
  );
}

You can see it in Action here

GitHub Releases Notes

GitHub Releases Notes

Key Features

🚀 Automatic Synchronization

Your release notes stay perfectly in sync with your GitHub releases without any manual intervention.

🎨 Rich Markdown Support

Full MDX compilation allows for rich formatting, code blocks, links, and even custom React components within your release notes.

⚡ Optimized Performance

  • SWR for intelligent caching and revalidation
  • Server-side API route to avoid CORS issues
  • Lazy loading and error boundaries

🔧 Highly Configurable

  • Customizable number of releases to display
  • Flexible styling with Mantine components
  • Easy integration with existing documentation sites

📱 Responsive Design

Beautiful, mobile-friendly interface that works across all devices.

Advanced Customization

Adding GitHub Token for Higher Rate Limits

For production use, add a GitHub personal access token:

# .env.local
GITHUB_TOKEN=your_github_token_here

Then uncomment the Authorization header in your API route.

Custom MDX Components

Enhance your release notes with custom components

const customComponents = {
  h2: ({ children }) => <Title order={2} mt="xl">{children}</Title>,
  code: ({ children }) => <Code>{children}</Code>,
  pre: ({ children }) => <CodeBlock>{children}</CodeBlock>,
};

<MDXRemote 
  compiledSource={release.body}
  components={customComponents}
/>

Conclusion

This implementation provides a robust, maintainable solution for displaying release notes that requires minimal setup and automatically stays synchronized with your GitHub releases. The combination of TypeScript for type safety, React hooks for state management, and beautiful UI components creates a professional changelog experience for your users.

The modular architecture makes it easy to customize and extend, while the server-side API route ensures reliable data fetching without exposing API keys to the client. Whether you’re building a documentation site, a project dashboard, or any application that needs to display version history, this approach offers the perfect balance of simplicity and functionality.


메타데이터
post_id
d88dcb5ebf0b
slug
building-dynamic-release-notes-with-github-api-a-complete-react-implementation-d88dcb5ebf0b
url
https://medium.com/@giovambattista.fazioli/building-dynamic-release-notes-with-github-api-a-complete-react-implementation-d88dcb5ebf0b
canonical_url
https://medium.com/@giovambattista.fazioli/building-dynamic-release-notes-with-github-api-a-complete-react-implementation-d88dcb5ebf0b
author_url
https://medium.com/@giovambattista.fazioli
status
ok
fetched_at
2026-09-06 02:56:50