26 KiB
Fork Configuration Guide
After forking this repo, update these files with your site information. Choose one of two options:
Option 1: Automated Script (Recommended)
Run a single command to configure all files automatically.
Step 1: Create your config file
cp fork-config.json.example fork-config.json
The file fork-config.json is gitignored, so your configuration stays local and is not committed. The .example file remains as a template.
Step 2: Edit fork-config.json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "A one-sentence description of your site.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Your bio text here.",
"theme": "tan"
}
Step 3: Run the configuration script
npm run configure
This updates all 11 configuration files automatically:
src/config/siteConfig.tssrc/pages/Home.tsxsrc/pages/Post.tsxconvex/http.tsconvex/rss.tsindex.htmlpublic/llms.txtpublic/robots.txtpublic/openapi.yamlpublic/.well-known/ai-plugin.jsonsrc/context/ThemeContext.tsx
Step 4: Review and deploy
git diff # Review changes
npx convex dev # Start Convex (if not running)
npm run sync # Sync content
npm run dev # Test locally
Option 2: Manual Configuration
Edit each file individually following the guide below.
Files to Update
| File | What to Update |
|---|---|
src/config/siteConfig.ts |
Site name, bio, GitHub username, gitHubRepo config, features |
src/pages/Home.tsx |
Intro paragraph, footer links |
src/pages/Post.tsx |
SITE_URL, SITE_NAME constants |
convex/http.ts |
SITE_URL, SITE_NAME constants |
convex/rss.ts |
SITE_URL, SITE_TITLE, SITE_DESCRIPTION |
index.html |
Meta tags, JSON-LD, page title |
public/llms.txt |
Site info, GitHub link |
public/robots.txt |
Sitemap URL |
public/openapi.yaml |
Server URL, site name |
public/.well-known/ai-plugin.json |
Plugin metadata |
src/context/ThemeContext.tsx |
Default theme |
Manual Configuration Details
1. src/config/siteConfig.ts
Update the main site configuration:
export const siteConfig: SiteConfig = {
name: "YOUR SITE NAME",
title: "YOUR TAGLINE",
logo: "/images/logo.svg", // or null to hide
intro: null,
bio: `YOUR BIO TEXT HERE.`,
// Featured section
featuredViewMode: "cards", // 'list' or 'cards'
showViewToggle: true,
// Logo gallery (set enabled: false to hide)
logoGallery: {
enabled: true,
images: [
{ src: "/images/logos/your-logo.svg", href: "https://example.com" },
],
position: "above-footer",
speed: 30,
title: "Built with",
scrolling: false,
maxItems: 4,
},
// GitHub contributions graph
gitHubContributions: {
enabled: true,
username: "YOURUSERNAME",
showYearNavigation: true,
linkToProfile: true,
title: "GitHub Activity",
},
// Visitor map (stats page)
visitorMap: {
enabled: true,
title: "Live Visitors",
},
// Blog page
blogPage: {
enabled: true,
showInNav: true,
title: "Blog",
description: "All posts from the blog, sorted by date.",
order: 2,
},
// Posts display
postsDisplay: {
showOnHome: true,
showOnBlogPage: true,
},
// Homepage configuration
// Set any page or blog post to serve as the homepage
homepage: {
type: "default", // Options: "default" (standard Home component), "page" (use a static page), or "post" (use a blog post)
slug: undefined, // Required if type is "page" or "post" - the slug of the page/post to use
originalHomeRoute: "/home", // Route to access the original homepage when custom homepage is set
},
links: {
docs: "/setup-guide",
convex: "https://convex.dev",
netlify: "https://netlify.com",
},
// GitHub repository config (for AI service links)
// Used by ChatGPT, Claude, Perplexity "Open in AI" buttons
gitHubRepo: {
owner: "YOURUSERNAME", // GitHub username or organization
repo: "YOUR-REPO-NAME", // Repository name
branch: "main", // Default branch
contentPath: "public/raw", // Path to raw markdown files
},
};
2. src/pages/Home.tsx
Update the intro paragraph (lines 96-108):
<p className="home-intro">
YOUR SITE DESCRIPTION HERE.{" "}
<a
href="https://github.com/YOURUSERNAME/YOUR-REPO"
target="_blank"
rel="noopener noreferrer"
className="home-text-link"
>
Fork it
</a>
, customize it, ship it.
</p>
Update the footer section (lines 203-271):
<section className="home-footer">
<p className="home-footer-text">
Built with{" "}
<a href={siteConfig.links.convex} target="_blank" rel="noopener noreferrer">
Convex
</a>{" "}
for real-time sync and deployed on{" "}
<a
href={siteConfig.links.netlify}
target="_blank"
rel="noopener noreferrer"
>
Netlify
</a>
. Read the{" "}
<a
href="https://github.com/YOURUSERNAME/YOUR-REPO"
target="_blank"
rel="noopener noreferrer"
>
project on GitHub
</a>{" "}
to fork and deploy your own. View{" "}
<a href="/stats" className="home-text-link">
real-time site stats
</a>
.
</p>
<p></p>
<br></br>
<p className="home-footer-text">
Created by{" "}
<a
href="https://x.com/YOURHANDLE"
target="_blank"
rel="noopener noreferrer"
>
YOUR NAME
</a>{" "}
with Convex, Cursor, and Claude. Follow on{" "}
<a
href="https://x.com/YOURHANDLE"
target="_blank"
rel="noopener noreferrer"
>
Twitter/X
</a>
,{" "}
<a
href="https://www.linkedin.com/in/YOURPROFILE/"
target="_blank"
rel="noopener noreferrer"
>
LinkedIn
</a>
, and{" "}
<a
href="https://github.com/YOURUSERNAME"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</a>
.
</p>
</section>
3. src/pages/Post.tsx
Update the site constants (lines 11-13):
const SITE_URL = "https://YOURSITE.netlify.app";
const SITE_NAME = "YOUR SITE NAME";
const DEFAULT_OG_IMAGE = "/images/og-default.svg";
4. convex/http.ts
Update the site configuration (lines 9-10):
const SITE_URL = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const SITE_NAME = "YOUR SITE NAME";
Also update the generateMetaHtml function (lines 233-234):
const siteUrl = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const siteName = "YOUR SITE NAME";
5. convex/rss.ts
Update the RSS configuration (lines 5-8):
const SITE_URL = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const SITE_TITLE = "YOUR SITE NAME";
const SITE_DESCRIPTION = "YOUR SITE DESCRIPTION HERE.";
6. index.html
Update all meta tags and JSON-LD structured data:
<!-- SEO Meta Tags -->
<meta name="description" content="YOUR SITE DESCRIPTION" />
<meta name="author" content="YOUR SITE NAME" />
<!-- Open Graph -->
<meta property="og:title" content="YOUR SITE NAME" />
<meta property="og:description" content="YOUR SITE DESCRIPTION" />
<meta property="og:url" content="https://YOURSITE.netlify.app/" />
<meta property="og:site_name" content="YOUR SITE NAME" />
<meta
property="og:image"
content="https://YOURSITE.netlify.app/images/og-default.svg"
/>
<!-- Twitter Card -->
<meta property="twitter:domain" content="YOURSITE.netlify.app" />
<meta property="twitter:url" content="https://YOURSITE.netlify.app/" />
<meta name="twitter:title" content="YOUR SITE NAME" />
<meta name="twitter:description" content="YOUR SITE DESCRIPTION" />
<meta
name="twitter:image"
content="https://YOURSITE.netlify.app/images/og-default.svg"
/>
<!-- JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "YOUR SITE NAME",
"url": "https://YOURSITE.netlify.app",
"description": "YOUR SITE DESCRIPTION"
}
</script>
<title>YOUR SITE TITLE</title>
7. public/llms.txt
Update site information:
# Site Information
- Name: YOUR SITE NAME
- URL: https://YOURSITE.netlify.app
- Description: YOUR SITE DESCRIPTION
# Links
- GitHub: https://github.com/YOURUSERNAME/YOUR-REPO
8. public/robots.txt
Update the header and sitemap URL:
# robots.txt for YOUR SITE NAME
Sitemap: https://YOURSITE.netlify.app/sitemap.xml
9. public/openapi.yaml
Update API title and server URL:
info:
title: YOUR SITE NAME API
contact:
url: https://github.com/YOURUSERNAME/YOUR-REPO
servers:
- url: https://YOURSITE.netlify.app
10. public/.well-known/ai-plugin.json
Update plugin metadata:
{
"name_for_human": "YOUR SITE NAME",
"name_for_model": "your_site_name",
"description_for_human": "YOUR SITE DESCRIPTION",
"contact_email": "you@example.com"
}
11. src/context/ThemeContext.tsx
Change the default theme (line 21):
const DEFAULT_THEME: Theme = "tan"; // Options: dark, light, tan, cloud
Homepage Configuration
You can set any page or blog post to serve as your homepage instead of the default Home component.
In fork-config.json
{
"homepage": {
"type": "page",
"slug": "about",
"originalHomeRoute": "/home"
}
}
Manual Configuration
In src/config/siteConfig.ts:
homepage: {
type: "page", // Options: "default", "page", or "post"
slug: "about", // Required if type is "page" or "post" - the slug of the page/post to use
originalHomeRoute: "/home", // Route to access the original homepage when custom homepage is set
},
Options
type:"default"(standard Home component),"page"(use a static page), or"post"(use a blog post)slug: The slug of the page or post to use (required if type is "page" or "post")originalHomeRoute: Route to access the original homepage (default: "/home")
Behavior
- Custom homepage uses the page/post's full content and features (sidebar, copy dropdown, footer, etc.)
- Featured section is NOT shown on custom homepage (only on default Home component)
- SEO metadata comes from the page/post's frontmatter
- Original homepage remains accessible at
/home(or configured route) when custom homepage is set - Back button is hidden when a page/post is used as the homepage
Examples
Use a static page as homepage:
homepage: {
type: "page",
slug: "about",
originalHomeRoute: "/home",
},
Use a blog post as homepage:
homepage: {
type: "post",
slug: "welcome-post",
originalHomeRoute: "/home",
},
Switch back to default homepage:
homepage: {
type: "default",
slug: undefined,
originalHomeRoute: "/home",
},
Newsletter Configuration
The newsletter feature integrates with AgentMail for email subscriptions and sending. It is disabled by default.
Environment Variables
Set these in the Convex dashboard:
| Variable | Description |
|---|---|
AGENTMAIL_API_KEY |
Your AgentMail API key |
AGENTMAIL_INBOX |
Your inbox address (e.g., newsletter@mail.agentmail.to) |
In fork-config.json
{
"newsletter": {
"enabled": true,
"agentmail": {
"inbox": "newsletter@mail.agentmail.to"
},
"signup": {
"home": {
"enabled": true,
"position": "above-footer",
"title": "Stay Updated",
"description": "Get new posts delivered to your inbox."
},
"blogPage": {
"enabled": true,
"position": "above-footer",
"title": "Subscribe",
"description": "Get notified when new posts are published."
},
"posts": {
"enabled": true,
"position": "below-content",
"title": "Enjoyed this post?",
"description": "Subscribe for more updates."
}
}
}
}
Manual Configuration
In src/config/siteConfig.ts:
newsletter: {
enabled: true, // Master switch for newsletter feature
agentmail: {
inbox: "newsletter@mail.agentmail.to",
},
signup: {
home: {
enabled: true,
position: "above-footer", // or "below-intro"
title: "Stay Updated",
description: "Get new posts delivered to your inbox.",
},
blogPage: {
enabled: true,
position: "above-footer", // or "below-posts"
title: "Subscribe",
description: "Get notified when new posts are published.",
},
posts: {
enabled: true,
position: "below-content",
title: "Enjoyed this post?",
description: "Subscribe for more updates.",
},
},
},
Frontmatter Override
Hide or show newsletter signup on specific posts using frontmatter:
---
title: My Post
newsletter: false # Hide newsletter signup on this post
---
Or force show it even if posts default is disabled:
---
title: Special Offer Post
newsletter: true # Show newsletter signup on this post
---
Sending Newsletters
To send a newsletter for a specific post:
npm run newsletter:send setup-guide
Or use the Convex CLI directly:
npx convex run newsletter:sendPostNewsletter '{"postSlug":"setup-guide","siteUrl":"https://yoursite.com","siteName":"Your Site"}'
Subscriber Management
View subscriber count on the /stats page. Subscribers are stored in the newsletterSubscribers table in Convex.
Newsletter Admin
The Newsletter Admin UI at /newsletter-admin provides a management interface for subscribers and sending newsletters.
Configuration:
In src/config/siteConfig.ts:
newsletterAdmin: {
enabled: true, // Enable /newsletter-admin route
showInNav: false, // Hide from navigation (access via direct URL)
},
Features:
- View and search all subscribers
- Filter by status (all, active, unsubscribed)
- Delete subscribers
- Send blog posts as newsletters
- Write and send custom emails with markdown support
- View recent newsletter sends
- Email statistics dashboard
CLI Commands:
# Send a blog post to all subscribers
npm run newsletter:send <post-slug>
# Send weekly stats summary
npm run newsletter:send:stats
Newsletter Notifications
Configure developer notifications for subscriber events:
In src/config/siteConfig.ts:
newsletterNotifications: {
enabled: true, // Global toggle for notifications
newSubscriberAlert: true, // Send email when new subscriber signs up
weeklyStatsSummary: true, // Send weekly stats summary email
},
Uses AGENTMAIL_CONTACT_EMAIL or AGENTMAIL_INBOX as recipient.
Weekly Digest
Automated weekly email with posts from the past 7 days:
In src/config/siteConfig.ts:
weeklyDigest: {
enabled: true, // Global toggle for weekly digest
dayOfWeek: 0, // 0 = Sunday, 6 = Saturday
subject: "Weekly Digest", // Email subject prefix
},
Runs automatically via cron job every Sunday at 9:00 AM UTC.
Contact Form Configuration
Enable contact forms on any page or post via frontmatter. Messages are sent via AgentMail.
Environment Variables
Set these in the Convex dashboard:
| Variable | Description |
|---|---|
AGENTMAIL_API_KEY |
Your AgentMail API key |
AGENTMAIL_INBOX |
Your inbox address for sending (e.g., newsletter@mail.agentmail.to) |
AGENTMAIL_CONTACT_EMAIL |
Optional: recipient for contact form messages (defaults to AGENTMAIL_INBOX) |
Site Config
In src/config/siteConfig.ts:
contactForm: {
enabled: true, // Global toggle for contact form feature
title: "Get in Touch",
description: "Send us a message and we'll get back to you.",
},
Note: Recipient email is configured via Convex environment variables (AGENTMAIL_CONTACT_EMAIL or AGENTMAIL_INBOX). Never hardcode email addresses in code.
Frontmatter Usage
Enable contact form on any page or post:
---
title: Contact Us
slug: contact
contactForm: true
---
The form includes name, email, and message fields. Submissions are stored in Convex and sent via AgentMail to the configured recipient.
Footer Configuration
The footer component displays markdown content and can be configured globally or per-page.
In fork-config.json
{
"footer": {
"enabled": true,
"showOnHomepage": true,
"showOnPosts": true,
"showOnPages": true,
"showOnBlogPage": true,
"defaultContent": "Built with [Convex](https://convex.dev) for real-time sync."
}
}
Manual Configuration
In src/config/siteConfig.ts:
footer: {
enabled: true, // Global toggle for footer
showOnHomepage: true, // Show footer on homepage
showOnPosts: true, // Default: show footer on blog posts
showOnPages: true, // Default: show footer on static pages
showOnBlogPage: true, // Show footer on /blog page
defaultContent: "...", // Default markdown content
},
Frontmatter Override:
Set showFooter: false in post/page frontmatter to hide footer on specific pages. Set footer: "..." to provide custom markdown content.
Social Footer Configuration
Display social icons and copyright information below the main footer.
In fork-config.json
{
"socialFooter": {
"enabled": true,
"showOnHomepage": true,
"showOnPosts": true,
"showOnPages": true,
"showOnBlogPage": true,
"socialLinks": [
{
"platform": "github",
"url": "https://github.com/yourusername/your-repo-name"
},
{
"platform": "twitter",
"url": "https://x.com/yourhandle"
}
],
"copyright": {
"siteName": "Your Site Name",
"showYear": true
}
}
}
Manual Configuration
In src/config/siteConfig.ts:
socialFooter: {
enabled: true,
showOnHomepage: true,
showOnPosts: true,
showOnPages: true,
showOnBlogPage: true,
socialLinks: [
{ platform: "github", url: "https://github.com/username" },
{ platform: "twitter", url: "https://x.com/handle" },
{ platform: "linkedin", url: "https://linkedin.com/in/profile" },
],
copyright: {
siteName: "Your Site Name",
showYear: true, // Auto-updates to current year
},
},
Supported Platforms: github, twitter, linkedin, instagram, youtube, tiktok, discord, website
Frontmatter Override:
Set showSocialFooter: false in post/page frontmatter to hide social footer on specific pages.
Right Sidebar Configuration
Enable a right sidebar on posts and pages that displays CopyPageDropdown at wide viewport widths.
In fork-config.json
{
"rightSidebar": {
"enabled": true,
"minWidth": 1135
}
}
Manual Configuration
In src/config/siteConfig.ts:
rightSidebar: {
enabled: true, // Set to false to disable globally
minWidth: 1135, // Minimum viewport width to show sidebar
},
Frontmatter Usage:
Enable right sidebar on specific posts/pages:
---
title: My Post
rightSidebar: true
---
Features:
- Right sidebar appears at 1135px+ viewport width
- Contains CopyPageDropdown with sharing options
- Three-column layout: left sidebar (TOC), main content, right sidebar
- Hidden below 1135px, CopyPageDropdown returns to nav
AI Chat Configuration
Configure the AI writing assistant powered by Anthropic Claude.
In fork-config.json
{
"aiChat": {
"enabledOnWritePage": false,
"enabledOnContent": false
}
}
Manual Configuration
In src/config/siteConfig.ts:
aiChat: {
enabledOnWritePage: true, // Show AI chat toggle on /write page
enabledOnContent: true, // Allow AI chat on posts/pages via frontmatter
},
Environment Variables (Convex):
ANTHROPIC_API_KEY(required): Your Anthropic API keyCLAUDE_PROMPT_STYLE,CLAUDE_PROMPT_COMMUNITY,CLAUDE_PROMPT_RULES(optional): Split system promptsCLAUDE_SYSTEM_PROMPT(optional): Single system prompt fallback
Frontmatter Usage:
Enable AI chat on posts/pages:
---
title: My Post
rightSidebar: true
aiChat: true
---
Requires rightSidebar: true and siteConfig.aiChat.enabledOnContent: true.
Posts Display Configuration
Control where posts appear and limit homepage display.
In fork-config.json
{
"postsDisplay": {
"showOnHome": true,
"showOnBlogPage": true,
"homePostsLimit": 5,
"homePostsReadMore": {
"enabled": true,
"text": "Read more blog posts",
"link": "/blog"
}
}
}
Manual Configuration
In src/config/siteConfig.ts:
postsDisplay: {
showOnHome: true, // Show post list on homepage
showOnBlogPage: true, // Show post list on /blog page
homePostsLimit: 5, // Limit posts on homepage (undefined = show all)
homePostsReadMore: {
enabled: true, // Show "read more" link when limited
text: "Read more blog posts",
link: "/blog",
},
},
AI Agent Prompt
Copy this prompt to have an AI agent apply all changes:
I just forked the markdown-site repo. Please update all configuration files with my site information:
Site Name: [YOUR SITE NAME]
Site Title/Tagline: [YOUR TAGLINE]
Site Description: [YOUR DESCRIPTION]
Site URL: https://[YOURSITE].netlify.app
GitHub Username: [YOURUSERNAME]
GitHub Repo: [YOUR-REPO]
Contact Email: [your@email.com]
Creator Info:
- Name: [YOUR NAME]
- Twitter: https://x.com/[YOURHANDLE]
- LinkedIn: https://www.linkedin.com/in/[YOURPROFILE]/
- GitHub: https://github.com/[YOURUSERNAME]
GitHub Repo Config (for AI service links):
- Owner: [YOURUSERNAME]
- Repo: [YOUR-REPO]
- Branch: main
- Content Path: public/raw
Update these files:
1. src/config/siteConfig.ts - site name, bio, GitHub username, gitHubRepo config
2. src/pages/Home.tsx - intro paragraph and footer section with all creator links
3. src/pages/Post.tsx - SITE_URL and SITE_NAME constants
4. convex/http.ts - SITE_URL and SITE_NAME constants
5. convex/rss.ts - SITE_URL, SITE_TITLE, SITE_DESCRIPTION
6. index.html - all meta tags, JSON-LD, title
7. public/llms.txt - site info and GitHub link
8. public/robots.txt - header comment and sitemap URL
9. public/openapi.yaml - API title, server URL, contact URL
10. public/.well-known/ai-plugin.json - plugin metadata and contact email
After Configuration
- Run
npx convex devto initialize Convex - Run
npm run syncto sync content to development - Run
npm run devto test locally - Deploy to Netlify when ready
Syncing Discovery Files
Discovery files (AGENTS.md and public/llms.txt) can be automatically updated with your current app data.
Commands
| Command | Description |
|---|---|
npm run sync:discovery |
Update discovery files with local Convex data |
npm run sync:discovery:prod |
Update discovery files with production Convex data |
npm run sync:all |
Sync content + discovery files together (development) |
npm run sync:all:prod |
Sync content + discovery files together (production) |
When to run
npm run sync: Run when you add, edit, or remove markdown contentnpm run sync:discovery: Run when you change site configuration or want to update discovery files with latest post countsnpm run sync:all: Run both syncs together (recommended for complete updates)
What gets updated
| File | Updated Content |
|---|---|
AGENTS.md |
Project overview, current status (site name, URL, post/page counts) |
public/llms.txt |
Site info, total posts, latest post date, GitHub URL |
The script reads from siteConfig.ts and queries Convex for live content statistics.
Optional: Content Files
Replace example content in:
| File | Purpose |
|---|---|
content/blog/*.md |
Blog posts |
content/pages/*.md |
Static pages (About, etc.) |
content/pages/home.md |
Homepage intro content (slug: home-intro, uses blog heading styles) |
public/images/logo.svg |
Site logo |
public/images/og-default.svg |
Default social share image |
public/images/logos/*.svg |
Logo gallery images |