Scribo is a standalone blog rendering application powered by the GrowPilot API. It's designed to be a turnkey solution for creating beautiful, SEO-optimized blogs that grow on autopilot.
- π Next.js 15 with App Router
- π¨ Tailwind CSS v4 for styling
- π Markdown rendering with inline images
- π Component injection system for customization
- π SEO-optimized with metadata and sitemaps
- π± Responsive design (desktop-first)
- β‘ Server Components for optimal performance
- π Smart URL routing for local dev vs production
- π― Alphabet filtering for glossaries
- π Loading states with skeleton components
- πΌοΈ Image optimization with Next.js Image
- π¨ Custom theming support
npm install
# or
pnpm installCreate a .env.local file with your GrowPilot API credentials:
# Required - GrowPilot API Configuration
GROWPILOT_API_KEY=your_api_key_here
NEXT_PUBLIC_GROWPILOT_DOMAIN=https://your-growpilot-instance.com
# URL Configuration (for local dev vs production)
# Main App URL (for signup, features, etc.)
# In production: https://yourdomain.com
# In local dev: http://localhost:3001 (your main app)
NEXT_PUBLIC_MAIN_APP_URL=https://yourdomain.com
# Blog App URL (for blog, topics, glossaries)
# In production: same as MAIN_APP_URL (routes are rewritten)
# In local dev: http://localhost:3033 (this Scribo app)
NEXT_PUBLIC_BLOG_APP_URL=https://yourdomain.com
# Legacy: Site URL (fallback for backward compatibility)
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
# Optional
NEXT_PUBLIC_IMAGE_DOMAINS=your-cdn.com,another-cdn.comnpm run devVisit http://localhost:3033 to see your blog.
Scribo uses a component injection system that allows you to customize every aspect of your blog without modifying the core code.
Create a config.customized.ts file to configure your blog:
import type { ScriboConfig } from './lib/config/types'
export const customConfig: Partial<ScriboConfig> = {
siteName: 'My Blog',
siteDescription: 'My awesome blog',
siteUrl: 'https://myblog.com',
ogImageUrl: 'https://myblog.com/images/og-image.png', // Optional: Custom OpenGraph image URL
api: {
growPilotApiKey: process.env.GROWPILOT_API_KEY || '',
growPilotDomain: process.env.NEXT_PUBLIC_GROWPILOT_DOMAIN || '',
},
}You can inject custom components to replace the defaults:
import type { ScriboConfig } from './lib/config/types'
import { MyFooter } from './components/my-footer'
import { MyHeader } from './components/my-header'
export const customConfig: Partial<ScriboConfig> = {
siteName: 'My Blog',
siteDescription: 'My awesome blog',
siteUrl: 'https://myblog.com',
ogImageUrl: 'https://myblog.com/images/og-image.png',
api: {
growPilotApiKey: process.env.GROWPILOT_API_KEY || '',
growPilotDomain: process.env.NEXT_PUBLIC_GROWPILOT_DOMAIN || '',
},
components: {
Header: MyHeader,
Footer: MyFooter,
CustomerLogos: MyCustomerLogos,
UpsellBanner: MyUpsellBanner,
BlogPageHero: MyBlogPageHero,
MidArticleUpsell: MyMidArticleCard,
BlogSummarizeCard: MySummarizeCard,
TryProductCard: MyProductCard,
BlogAuthorInfo: MyAuthorInfo,
},
}| Component | Props | Description |
|---|---|---|
Header |
none | Site header/navigation |
Footer |
none | Site footer |
CustomerLogos |
{ variant?: 'default' | 'small' } |
Customer logo showcase (supports small variant) |
UpsellBanner |
none | Bottom-of-page CTA banner |
BlogPageHero |
none | Hero section on blog listing page |
MidArticleUpsell |
none | Upsell card in the middle of blog posts |
BlogSummarizeCard |
{ blogPostId: string } |
Post summarization card |
TryProductCard |
none | Product trial/signup card |
BlogAuthorInfo |
{ lastUpdatedAt: string } |
Author information sidebar |
Scribo includes a smart URL configuration system that handles different scenarios:
- Main App:
http://localhost:3001(your main application) - Blog App:
http://localhost:3033(this Scribo instance)
- Main App:
https://yourdomain.com(your main application) - Blog App:
https://yourdomain.com(same domain, routes rewritten)
NEXT_PUBLIC_MAIN_APP_URL- Points to your main app (signup, features, etc.)NEXT_PUBLIC_BLOG_APP_URL- Points to blog app (blog, topics, glossaries)NEXT_PUBLIC_SITE_URL- Legacy fallback for backward compatibility
The system automatically handles:
- Signup/login links β Main app
- Blog navigation β Blog app (local) or main app (production)
- Component injection β Uses appropriate URLs based on environment
To route blog traffic from your main app to Scribo in production, add these rewrites to your main app's next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/blog',
destination: 'https://your-scribo-domain.com/blog',
},
{
source: '/blog/:path*',
destination: 'https://your-scribo-domain.com/blog/:path*',
},
{
source: '/topics',
destination: 'https://your-scribo-domain.com/topics',
},
{
source: '/topics/:path*',
destination: 'https://your-scribo-domain.com/topics/:path*',
},
{
source: '/glossaries',
destination: 'https://your-scribo-domain.com/glossaries',
},
{
source: '/glossaries/:path*',
destination: 'https://your-scribo-domain.com/glossaries/:path*',
},
]
},
}
module.exports = nextConfigNote: Replace https://your-scribo-domain.com with your actual Scribo deployment URL (e.g., https://blog.yourdomain.com or your Vercel deployment URL like https://your-blog.vercel.app).
Scribo includes automatic SEO protection to prevent duplicate content issues. The robots.ts file automatically detects if it's running on the shadow deployment or main domain:
- Shadow Deployment (e.g.,
scribo.vercel.app): Blocks all search engines withDisallow: / - Main Domain (e.g.,
yourdomain.com): Allows indexing and includes sitemap
The detection works by comparing NEXT_PUBLIC_SITE_URL with NEXT_PUBLIC_MAIN_APP_URL:
- If they're different β Shadow deployment β Block crawlers
- If they're the same β Main deployment β Allow crawlers
This ensures only your main domain URLs (accessed via rewrites) get indexed in search engines, preventing duplicate content penalties.
scribo/
βββ app/ # Next.js App Router
β βββ blog/ # Blog routes
β βββ topics/ # Topic routes
β βββ glossaries/ # Glossary routes
βββ components/
β βββ default/ # Default components (generic)
β βββ innerview/ # Example: Innerview-specific components
β βββ ui/ # Core UI components
βββ lib/
β βββ api/ # GrowPilot API integration
β βββ config/ # Configuration system
β βββ env/ # Environment utilities
βββ public/ # Static assets
See config.customized.ts for a complete example of how Innerview configures Scribo with custom branding and components.
- Push your code to GitHub
- Import your repository in Vercel
- Add your environment variables
- Deploy!
Scribo is a standard Next.js 15 application and can be deployed anywhere Next.js is supported:
- Netlify
- AWS Amplify
- Google Cloud Run
- Self-hosted with Docker
Scribo requires a GrowPilot API key since it calls the following endpoints:
GET /api/v1/posts- List blog postsGET /api/v1/posts/:slug- Get single postGET /api/v1/topics- List topicsGET /api/v1/topics/:slug/posts- Get posts by topicGET /api/v1/glossaries- List glossariesGET /api/v1/glossaries/:slug- Get glossary termsGET /api/v1/glossaries/:slug/:termSlug- Get glossary term details
MIT