FumaDocs React Component to Display Code from Github Repos
Rajiv Seelam

Rajiv Seelam @rjvim

Joined:
Nov 16, 2021

FumaDocs React Component to Display Code from Github Repos

Publish Date: Apr 17
5 2

The GithubCodeBlock component allows you to embed and display code from GitHub repositories directly in your Fuma Docs application. It supports line extraction, syntax highlighting, and line highlighting features.

Installation

You need to have fumadocs setup ready and running

Step 1: Copy following code anywhere into your components

import * as Base from "fumadocs-ui/components/codeblock";
import { highlight } from "fumadocs-core/highlight";
import { transformerMetaHighlight } from "@shikijs/transformers";

// Types
export interface CodeBlockProps {
  code: string;
  wrapper?: Base.CodeBlockProps;
  lang: string;
  highlightLines?: string;
}

interface GithubCodeBlockProps {
  url: string;
  extractLines?: boolean;
  highlightLines?: string;
  wrapper?: Base.CodeBlockProps;
}

interface GitHubReference {
  rawUrl: string;
  fromLine?: number;
  toLine?: number;
  highlightLines?: string;
}

// Helper functions
function formatHighlightLines(highlightLines?: string): string | undefined {
  if (!highlightLines) return undefined;
  return highlightLines.startsWith("{") && highlightLines.endsWith("}")
    ? highlightLines
    : `{${highlightLines}}`;
}

function getLanguageFromUrl(url: string): string {
  try {
    return url.split(".").pop()?.toLowerCase() || "";
  } catch {
    return "";
  }
}

function parseGitHubUrl(url: string): GitHubReference {
  try {
    // Split the URL to separate the line reference part
    const [githubUrl, loc] = url.split("#");

    if (!githubUrl) {
      throw new Error("Invalid GitHub URL");
    }

    // Initialize line reference variables
    let fromLine: number | undefined;
    let toLine: number | undefined;
    let highlightLines: string | undefined;

    // Parse line references if present
    if (loc) {
      const lineParts = loc.split("-");

      if (lineParts[0]?.startsWith("L")) {
        fromLine = parseInt(lineParts[0].slice(1), 10) - 1;

        if (lineParts[1]?.startsWith("L")) {
          toLine = parseInt(lineParts[1].slice(1), 10) - 1;
        } else {
          toLine = fromLine;
        }

        // Always generate highlight lines from location
        // These will be used if no explicit highlightLines prop is provided
        if (fromLine !== undefined && toLine !== undefined) {
          const startLine = fromLine + 1;
          const endLine = toLine + 1;
          highlightLines =
            startLine === endLine
              ? `{${startLine}}`
              : `{${startLine}-${endLine}}`;
        }
      }
    }

    // Parse GitHub URL to create raw URL
    const urlObj = new URL(githubUrl);
    const pathParts = urlObj.pathname.split("/").slice(1);

    if (pathParts.length < 5) {
      throw new Error("Invalid GitHub repository path");
    }

    const [org, repo, _, branch, ...pathSeg] = pathParts;

    if (!org || !repo || !branch || pathSeg.length === 0) {
      throw new Error("Missing required GitHub path components");
    }

    // Create reference object with raw URL and line info
    return {
      rawUrl: `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${pathSeg.join("/")}`,
      fromLine,
      toLine,
      highlightLines,
    };
  } catch (error) {
    console.error("Error parsing GitHub URL:", error);
    throw new Error(
      `Invalid GitHub URL: ${error instanceof Error ? error.message : String(error)}`
    );
  }
}

async function fetchCode(url: string, fromLine?: number, toLine?: number) {
  try {
    const response = await fetch(url, { cache: "force-cache" });

    if (!response.ok) {
      throw new Error(
        `Failed to fetch code: ${response.status} ${response.statusText}`
      );
    }

    const content = await response.text();

    // Return full content if no line numbers are specified
    if (fromLine === undefined || toLine === undefined) {
      return content;
    }

    // Extract specific lines
    const lines = content.split("\n");
    const selectedLines = lines.slice(fromLine, toLine + 1);

    if (selectedLines.length === 0) {
      return content;
    }

    // Calculate common indentation to remove
    const commonIndent = selectedLines.reduce(
      (indent: number, line: string) => {
        if (line.length === 0) return indent;
        const spaces = line.match(/^\s+/);
        return spaces ? Math.min(indent, spaces[0].length) : 0;
      },
      Infinity
    );

    // Remove common indentation and join lines
    return selectedLines
      .map((line) => {
        if (line.length === 0) return line;
        return line.slice(commonIndent < Infinity ? commonIndent : 0);
      })
      .join("\n");
  } catch (error) {
    console.error("Error fetching code:", error);
    return `// Error fetching code: ${error instanceof Error ? error.message : String(error)}`;
  }
}

// Components
export async function CodeBlock({
  code,
  lang,
  wrapper,
  highlightLines,
}: CodeBlockProps) {
  const rendered = await highlight(code, {
    lang,
    meta: highlightLines ? { __raw: highlightLines } : undefined,
    themes: {
      light: "github-light",
      dark: "github-dark",
    },
    components: {
      pre: Base.Pre,
    },
    transformers: [transformerMetaHighlight()],
  });

  return <Base.CodeBlock {...wrapper}>{rendered}</Base.CodeBlock>;
}

export default async function GithubCodeBlock({
  url,
  extractLines = false,
  highlightLines,
  wrapper,
}: GithubCodeBlockProps) {
  try {
    // Validate GitHub URL
    if (!url.includes("github.com")) {
      throw new Error("This component only supports GitHub URLs");
    }

    // Parse GitHub URL to get raw URL and line info
    const reference = parseGitHubUrl(url);

    // Format highlight lines for Shiki
    // Priority: explicitly provided highlightLines prop > lines from URL loc
    const formattedHighlightLines = formatHighlightLines(
      highlightLines || reference.highlightLines
    );

    // Fetch the code content, extracting specific lines if needed
    const code = await fetchCode(
      reference.rawUrl,
      extractLines ? reference.fromLine : undefined,
      extractLines ? reference.toLine : undefined
    );

    const lang = getLanguageFromUrl(reference.rawUrl);

    return (
      <CodeBlock
        lang={lang}
        code={code}
        highlightLines={formattedHighlightLines}
        wrapper={wrapper}
      />
    );
  } catch (error) {
    console.error("Error in GithubCodeBlock:", error);
    return (
      <CodeBlock
        lang="text"
        code={`// Error: ${error instanceof Error ? error.message : String(error)}`}
        wrapper={wrapper}
      />
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Add to MDX Components

import defaultMdxComponents from "fumadocs-ui/mdx";
import type { MDXComponents } from "mdx/types";
import GithubCodeBlock from "./components/github-code-block";

export function getMDXComponents(components?: MDXComponents): MDXComponents {
  return {
    ...defaultMdxComponents,
    GithubCodeBlock: GithubCodeBlock,
    ...components,
  };
}
Enter fullscreen mode Exit fullscreen mode

Basic Usage

<GithubCodeBlock url="https://github.com/rjvim/react-component-library-starter/blob/main/lerna.json" />
Enter fullscreen mode Exit fullscreen mode

Props

The component accepts the following props:

Prop Type Default Description
url string (required) GitHub URL to the file you want to display
extractLines boolean false Whether to extract specific lines from the file
highlightLines string undefined Lines to highlight in the format "{1,3-4}"
wrapper object undefined CodeBlockProps to pass to the underlying CodeBlock component

Examples

Display a Complete File

<GithubCodeBlock url="https://github.com/rjvim/react-component-library-starter/blob/main/lerna.json" />
Enter fullscreen mode Exit fullscreen mode

Extract Specific Lines

You can extract specific lines from a file by adding a line reference to the URL and setting extractLines to true:

<GithubCodeBlock
  url="https://github.com/rjvim/react-component-library-starter/blob/main/lerna.json#L2-L4"
  extractLines={true}
/>
Enter fullscreen mode Exit fullscreen mode

Highlight Specific Lines

You can highlight specific lines using the highlightLines prop:

<GithubCodeBlock
  url="https://github.com/rjvim/react-component-library-starter/blob/main/lerna.json"
  highlightLines="{2,4}"
/>
Enter fullscreen mode Exit fullscreen mode

Launch Post

Note: In dev.to to editor I am not able to show the output of using the component, you can check that on: https://rjv.im/blog/solution/github-code-block

Comments 2 total

  • Nevo David
    Nevo DavidApr 17, 2025

    Sick how you can pull code straight from GitHub and show just the lines you want, kinda like clipping out only the best part of a picture, but it's pretty good. How might grabbing live code like this help teams keep docs and code more in sync?

    • Rajiv Seelam
      Rajiv SeelamApr 22, 2025

      How might grabbing live code like this help teams keep docs and code more in sync?

      I was writing documentation on how to setup functionality, but the source code was in another repo and docs were in another repo. And I found myself improving the source code itself - copy/pasting every-time was getting complex.

      I got this idea from docusaurus !!raw-loader, where you can load any file from local filesystem. This is possible using fumadocs too using tags. I just improvised over those to pull raw content from github.

      Image description

      I need to prepare examples like above, to be shown to user along with preview. So, these are my parts to build the final component. ShadCN setup seems complicated to me, as in, in that they read file using "fs", and format it and store in some other files to cache etc., I tried that but was constantly getting errors cuz of implementation issues. So, I went with above.

Add comment