A modern, production-ready documentation website that fetches and displays markdown files from multiple GitHub repositories. Built with React, TypeScript, and Vite. Features clean design, multi-repository support, and GitHub Flavored Markdown (GFM) rendering.
- React 18 + TypeScript for type-safe development
- Vite for fast builds and development
- Tailwind CSS for responsive styling
- GitHub Flavored Markdown with syntax highlighting (Prism.js)
- Multi-Repository Support - Browse documentation from multiple GitHub repos
- Theme switching (Light/Dark mode)
- Full-text search across all repositories
- File tree navigation with lazy loading
- Table of contents auto-generation
- GitHub Pages deployment ready
- Centralized Configuration - All site info configurable from one JSON file
dochub/
├── src/
│ ├── components/ # React components
│ │ ├── ui/ # shadcn/ui components
│ │ ├── DocumentView.tsx # Document view component
│ │ ├── FileTree.tsx # File tree component
│ │ ├── MarkdownViewer.tsx # Markdown viewer component
│ │ ├── RepositorySelector.tsx # Repository selector component
│ │ ├── SEOHead.tsx # Dynamic SEO meta tags
│ │ └── ...
│ ├── config/ # Configuration files
│ │ ├── site.json # Centralized site configuration
│ │ ├── config.ts # Config exports
│ │ └── repositories.ts # Repository configurations
│ ├── hooks/ # Custom React hooks
│ │ └── useTheme.tsx # Theme hook
│ ├── lib/ # Libraries
│ │ └── marked/ # Markdown renderer (from blog)
│ ├── pages/ # Page components
│ ├── styles/ # CSS files
│ │ └── markdown.css # Markdown styling (from blog)
│ ├── utils/ # Utility functions
│ │ └── github.ts # GitHub API utilities
│ │ └── cache.ts # Cache utilities
│ └── App.tsx # Main application component
├── public/
│ └── repositories.json # Repository config (optional)
└── package.json # Package configuration
All website information is configurable from a single JSON file: src/config/site.json
This centralized configuration system makes it easy to update site information, SEO settings, and features without modifying multiple files.
The src/config/site.json file contains:
-
Site Information (
site)name: Full site nameshortName: Short name for mobile/PWAdescription: Site descriptionurl: Base URL of the sitebasePath: Base path for routing (usually/)ogImage: Open Graph image URLfavicon: Favicon path
-
SEO Configuration (
seo)title: Page titledescription: Meta descriptionkeywords: SEO keywords (comma-separated)author: Site authorgoogleSiteVerification: Google Search Console verification coderobots: Robots meta tag contentcanonical: Canonical URL
-
Personal Information (
personal)name: Your nametitle: Your professional titlebio: Short biolocation: Locationavatar: Avatar image pathemail: Email address
-
Website Links (
websites)portfolio: Portfolio URLblog: Blog URLpublicProfile: Public profile URLdocumentationHub: Documentation hub URL
-
Social Links (
social)github: GitHub profile URLlinkedin: LinkedIn profile URLtwitter: Twitter/X profile URLemail: Email linkwebsite: Personal website URL
-
Manifest Configuration (
manifest)- PWA manifest settings (name, description, theme colors, etc.)
-
Features Configuration (
features)enableSearch: Enable/disable search featureenableDarkMode: Enable/disable dark modeenableTableOfContents: Enable/disable TOCenableRepositorySwitching: Enable/disable repo switchingsearchPlaceholder: Search input placeholder textdefaultTheme: Default theme (light,dark, orsystem)
Import config exports:
import { SITE_CONFIG, SEO_CONFIG, FEATURES_CONFIG, PERSONAL_INFO, SOCIAL_LINKS } from '@/config/config';Examples:
// Use site name
<SITE_CONFIG.name>
// Use SEO description
<SEO_CONFIG.description>
// Use feature flags
{FEATURES_CONFIG.enableSearch && <SearchComponent />}
// Use personal info
<PERSONAL_INFO.name>
// Use social links
<a href={SOCIAL_LINKS.github}>GitHub</a>The SEOHead component automatically updates meta tags based on the configuration:
import SEOHead from '@/components/SEOHead';
// In your App component
<SEOHead />This component:
- Updates document title
- Sets all SEO meta tags
- Updates Open Graph tags
- Updates Twitter Card tags
- Sets canonical URL
Simply edit src/config/site.json to update:
- Site name and description
- SEO meta tags
- Social media links
- Personal information
- Feature toggles
The changes will automatically be reflected throughout the application.
Note: Keep public/manifest.json in sync with the manifest section in site.json when making changes.
Repositories are configured in src/config/repositories.ts:
export const repositories: RepositoryConfig[] = [
{
id: 'dsa',
name: 'Data Structures & Algorithms',
owner: 'thisiskushal31',
repo: 'Datastructures-and-Algorithms',
branch: 'main',
description: 'DSA notes and solutions',
icon: '📚'
},
// Add more repositories...
];Edit src/config/repositories.ts and add a new repository configuration:
export const repositories: RepositoryConfig[] = [
// ... existing repositories
{
id: 'new-repo',
name: 'New Repository',
owner: 'username',
repo: 'repo-name',
branch: 'main',
description: 'Repository description',
icon: '📦'
},
];The website will automatically:
- Fetch the file tree from the repository
- Allow browsing markdown files
- Convert relative image paths to GitHub raw URLs
- Include the repository in search results
The dev server automatically checks and clones repositories if needed:
# Install dependencies
npm install
# Start development server (automatically clones repos if needed)
npm run devWhat happens:
- ✅ Checks if
public/repos/exists with all repositories - ✅ If missing, automatically clones all repos from
src/config/repositories.ts - ✅ Shows status messages during cloning
- ✅ Starts dev server (uses local files if available, falls back to API if cloning failed)
You'll see:
🔍 Checking for local repository files...✅ Local repositories found. Starting dev server...(if repos exist)- OR
📦 Local repositories not found.→🚀 Cloning repositories...(if missing)
For a production-like experience with static files:
# Install dependencies
npm install
# Clone repositories and copy files (optional, for local dev)
npm run clone-repos
# Start development server
npm run devThis will:
- Clone all repositories from
src/config/repositories.ts - Copy markdown files to
public/repos/ - Generate file tree JSON files
- Serve files locally (no API calls needed)
When source repositories are updated, you can update the cloned files:
# Update all cloned repositories with latest changes
npm run update-reposThis will:
- Pull latest changes from each source repository
- Update markdown files in
public/repos/ - Regenerate file tree JSON files
- Preserve existing structure (only updates changed files)
Note: The CI/CD workflow automatically updates repositories on each deployment, so manual updates are only needed for local development.
npm run dev
npm run build
npm run preview
## 🎨 Markdown Rendering
The markdown rendering system is copied from the [blog repository](https://github.com/thisiskushal31/blog):
- Uses `marked` library for parsing
- Prism.js for syntax highlighting
- DOMPurify for HTML sanitization
- Supports GitHub Flavored Markdown (tables, strikethrough, task lists)
- Custom embeds: YouTube and GitHub Gists
- Linkable headers with anchor links
## 📝 File Naming & Display
The documentation hub automatically processes file names for cleaner display in the sidebar. Here's how it works:
### Display Name Processing
The `getFileDisplayName()` function cleans up filenames in 3 steps:
1. **Remove file extension**: Strips `.md`, `.mdx`, or `.markdown`
2. **Replace separators**: Converts `-` and `_` to spaces
3. **Remove leading numbers**: Strips numbered prefixes like `01-`, `02_`, etc.
### Examples
| Actual Filename | Display Name |
|----------------|--------------|
| `01-Array_&_String.md` | `Array & String` |
| `02-Linked_List.md` | `Linked List` |
| `docker.md` | `docker` |
| `README.md` | `README` |
| `my-awesome-guide.md` | `my awesome guide` |
| `10_Binary_Tree.md` | `Binary Tree` |
### How File Mapping Works
The system uses two pieces of information from GitHub's API:
- **`node.name`**: Just the filename (e.g., `"01-Array_&_String.md"`)
- **`node.path`**: Full path from repo root (e.g., `"DataStructures/01-Array_&_String.md"`)
**Important**:
- The **display name** is only for UI presentation
- The **actual file path** (`node.path`) is always used to fetch content from GitHub
- This ensures files are fetched correctly regardless of how they're displayed
### File Selection Flow
```typescript
// 1. GitHub API returns file with both name and path
{
name: "01-Array_&_String.md", // Just filename
path: "DataStructures/01-Array_&_String.md", // Full path
type: "file"
}
// 2. Display: Clean up the name for UI
displayName = getFileDisplayName(node.name)
// Result: "Array & String"
// 3. Click: Use the FULL PATH to fetch the file
onSelectFile(node.path) // Uses "DataStructures/01-Array_&_String.md"
The system handles any filename format:
- ✅ Numbered prefixes:
01-file.md,02_file.md - ✅ No prefixes:
docker.md,README.md - ✅ Dashes and underscores:
my-file.md,my_file.md - ✅ Mixed formats:
01-My_File.md
All formats work correctly - the display name is cleaned for readability, but the actual file path is always preserved for fetching.
- Files are served from static copies in
public/repos/{repoId}/ - These are cloned and copied during CI/CD build
- Fast, no rate limits, works offline
- Updates when you push to main branch (triggers CI)
- Fetches fresh content from
raw.githubusercontent.com - Uses GitHub API (rate limited, but only when explicitly requested)
- Updates cache for faster future access
- Shows latest changes immediately
- Updates or clones all repositories from
src/config/repositories.ts(usesupdate-repos.jsif repos exist, otherwiseclone-repos.js) - Copies markdown files to
public/repos/{repoId}/ - Generates
tree.jsonfiles for file tree navigation - Generates sitemap.xml
- Builds the site with static files
- Deploys to GitHub Pages
To update content:
- Automatic (Scheduled): The workflow
update-content.yamlruns daily at 2 AM UTC to check for updates - Manual (GitHub Actions): Go to Actions → "Update Content from Source Repositories" → "Run workflow" to manually trigger an update
- Manual (Local): Run
npm run update-reposto update cloned repositories locally, then commit and push the updatedpublic/repos/directory - Via Webhook: Set up a webhook in your source repositories to trigger
repository_dispatchevents (see setup below)
Method 1: Refresh Button (Recommended)
- Open the document you want to update
- Click the "Refresh" button in the top-right corner
- This forces a fresh fetch from GitHub, bypassing all caches
Method 2: Reload Page
- Simply reload the browser page (F5 or Cmd+R)
- Navigate back to the document
Method 3: Hard Refresh
- Use Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
- This clears browser cache and fetches fresh content
The app includes cache-busting mechanisms:
- Query Parameters: When you click "Refresh", a timestamp is added to the URL
- Fetch Options: Uses
cache: 'no-store'for forced refreshes
- ✅ Bypasses browser cache
- ✅ Bypasses GitHub CDN cache
- ✅ Shows last update time
- ✅ Provides visual feedback (spinner while loading)
- ✅ Always fetches latest content from GitHub
The documentation hub can automatically update when source repositories change. Here are the available methods:
- The
update-content.yamlworkflow runs daily at 2 AM UTC - Automatically checks for updates in all source repositories
- Updates and redeploys if changes are found
- No action needed - works automatically
- Go to Actions tab in the
dochubrepository - Select "Update Content from Source Repositories"
- Click "Run workflow" → "Run workflow"
- This will immediately check for updates and redeploy
To trigger updates immediately when you push to source repositories:
-
Go to your source repository (e.g.,
Datastructures-and-Algorithms) -
Settings → Webhooks → Add webhook
-
Configure:
- Payload URL:
https://api.github.com/repos/thisiskushal31/dochub/dispatches - Content type:
application/json - Secret: (optional)
- Events: Select "Just the push event"
- Active: ✅ Checked
- Payload URL:
-
Add authentication (if needed):
- The workflow uses
GITHUB_TOKENwhich should work automatically - For custom tokens, add as secret
SOURCE_REPO_TOKEN
- The workflow uses
Note: The scheduled daily update ensures content stays fresh even without webhooks.
© 2025 Kushal Gupta. All Rights Reserved.
The source code and all content within this repository are the exclusive property of Kushal Gupta. This content is not to be used, reproduced, or distributed without explicit permission.
A public repository containing the website code for my personal documentation hub. All code is for my exclusive use and is not licensed for public distribution.
Live Site: https://thisiskushal31.github.io/dochub/