This document describes WeWrite's SEO strategy for user-generated content (UGC) - ensuring pages created by users are properly indexed by search engines.
For the latest end-to-end findings and prioritized remediation plan, see:
docs/seo/SEO_GEO_AUDIT_2026-04-29.md
That audit includes technical SEO fixes applied on April 29, 2026 (private-page indexing hardening, utility-route noindex directives, and sitemap-index route correction), plus a GEO readiness plan.
- Title tags: Dynamic titles based on page content (e.g., "My Article by username")
- Meta descriptions: First 160 characters of page content
- Canonical URLs: Properly set to
https://www.getwewrite.app/{pageId} - Open Graph tags: Full OG implementation for social sharing
- Twitter cards: Summary large image cards
- Article schema: Each page has Article structured data with:
headline,description,datePublished,dateModifiedauthor(Person) orpublisher(Organization for group pages)mainEntityOfPagewith canonical URL
- BreadcrumbList schema: Navigation hierarchy for better SERP display
- Home > Author > Page Title (for user pages)
- Home > Group > Page Title (for group pages)
- Main sitemap (
/sitemap.xml): Static pages and navigation (app/sitemap.ts) - Pages sitemap index (
/api/sitemap-pages-index): Cursor-paginated index for public page sitemap batches - Pages sitemap (
/api/sitemap-pages): Public user page chunks referenced by the pages sitemap index - Users sitemap (
/api/sitemap-users): Active user profiles - News sitemap (
/api/sitemap-news): Recent content (configurable via?days=N, default: last 2 days) - Sitemap index (
/api/sitemap-index): Aggregates all sitemaps
Important: The /sitemap.xml file only contains static pages. Dynamic content pages are served via /api/sitemap-pages-index and /api/sitemap-pages. Pages disallowed in robots.txt (e.g., /search, /activity) are excluded from the sitemap.
Priority is dynamic based on view count:
-
1000 views: 0.8
-
100 views: 0.7
-
10 views: 0.6
- Default: 0.5
- Allows crawling of public content:
/,/u/,/user/,/group/,/trending, etc. - Blocks private areas:
/admin/,/settings/,/notifications/,?edit=true,?private=true - Includes all sitemap references
- Sets
crawlDelay: 1to be respectful to crawlers
- Provides canonical domain and citation guidance for AI systems
- Points crawlers to primary sitemap and sitemap index endpoints
- Clarifies utility/auth surfaces to avoid citing for content discovery
- Dynamic OG image generation for each page
- Shows: Title, content preview (with styled links), author, sponsor count
- 1200x630 resolution
- Fallback to WeWrite branding if page not found
Solution: We render page content server-side using the ServerContentForSEO component, which is included alongside the client-side interactive editor.
// In app/[id]/page.tsx (success case)
return (
<>
{/* Server-rendered content for SEO crawlers */}
{pageData && (
<ServerContentForSEO
title={pageData.title || 'Untitled'}
content={pageData.content}
authorUsername={pageData.authorUsername || pageData.username}
createdAt={pageData.created}
lastModified={pageData.lastModified}
pageId={id}
// Engagement stats for Schema.org interactionStatistic
viewCount={pageData.viewCount || pageData.views}
sponsorCount={pageData.sponsorCount}
replyCount={pageData.replyCount}
/>
)}
{/* Interactive client component */}
<Suspense fallback={<ContentPageSkeleton />}>
<ContentPageClient pageId={id} initialStatus="page" initialPageData={pageData} />
</Suspense>
</>
);Data sources (fetched in app/api/pages/[id]/route.ts):
viewCount: From page documentsponsorCount: Count of unique users who allocated USD to this pagereplyCount: Count of non-deleted pages that reply to this page
How it works:
- Content is rendered as semantic HTML with Schema.org microdata
- Uses
sr-onlyclass (visually hidden but accessible to crawlers) - Includes
<noscript>fallback for crawlers that don't execute JS - Parses Slate.js content structure into plain HTML
- Preserves internal links for crawl discovery
What's included in the SEO content:
- Article headline and description
- Word count
- Canonical URL
- Published/modified dates (both
datePublishedanddateModified) - Author information (when available)
- Publisher information (WeWrite)
- Full article body as semantic HTML
- Engagement metrics via Schema.org
interactionStatistic:ReadActionwith view count (signals popularity)DonateActionwith sponsor count (signals trust/quality)CommentActionwith reply count +commentCountproperty
Add more schema types for better rich results:
{
"@context": "https://schema.org",
"@type": "Article",
"articleBody": "Full text content here...",
"wordCount": 1500,
"image": {
"@type": "ImageObject",
"url": "https://www.getwewrite.app/{id}/opengraph-image"
},
"interactionStatistic": {
"@type": "InteractionCounter",
"interactionType": "https://schema.org/ReadAction",
"userInteractionCount": 1234
}
}- FAQ Schema for pages with Q&A content
- HowTo Schema for tutorial/guide pages
- Review Schema if pages have ratings/reviews
- Internal linking signals - expose outgoing/incoming link counts
- Core Web Vitals - Monitor LCP/FID/CLS for content pages
- Mobile-first indexing - Ensure content looks good on mobile Googlebot
- Pagination for long content - Consider
rel="next"/rel="prev" - Hreflang - If supporting multiple languages
-
Google Search Console
- URL Inspection tool for individual pages
- Coverage report for indexing status
- Rich results report for structured data
-
Google Rich Results Test
- https://search.google.com/test/rich-results
- Test individual URLs for schema validation
-
Schema Markup Validator
- https://validator.schema.org/
- Validate JSON-LD syntax
-
Mobile-Friendly Test
-
Lighthouse
- SEO audit in Chrome DevTools
# View rendered HTML without JS
curl -A "Googlebot" https://www.getwewrite.app/{pageId}
# Check if content is in initial HTML
curl https://www.getwewrite.app/{pageId} | grep -i "page content text"| File | Purpose |
|---|---|
app/[id]/page.tsx |
Page component with metadata generation |
app/[id]/layout.tsx |
JSON-LD structured data |
app/[id]/opengraph-image.tsx |
Dynamic OG image generation |
app/[id]/ContentPageClient.tsx |
Client-side content rendering |
app/components/seo/ServerContentForSEO.tsx |
Server-rendered SEO content |
app/sitemap.ts |
Static pages sitemap |
app/robots.ts |
Crawler directives |
app/utils/sitemapGenerator.ts |
Dynamic sitemap generation |
app/api/sitemap-pages-index/route.ts |
Cursor-paginated pages sitemap index |
app/api/sitemap-pages/route.ts |
Pages sitemap API |
app/api/sitemap-users/route.ts |
Users sitemap API |
app/api/sitemap-news/route.ts |
News sitemap API |
public/llms.txt |
LLM crawler citation guidance |
public/llms-full.txt |
Extended GEO guidance |
scripts/seo-audit.js |
Repeatable SEO/GEO health checks |
- Index Coverage - % of public pages indexed
- Impressions - Search visibility
- Click-through Rate - SERP performance
- Rich Results - Schema markup adoption
Monitor performance for:
- Brand queries: "wewrite"
- Content queries: page titles, topics
- Author queries: username searches
WeWrite's SEO implementation is designed to work well with AI-powered search engines:
- Content is rendered server-side via
ServerContentForSEO.tsx - Full text content is available in initial HTML response
- No JavaScript required to read page content
- Rich Schema.org markup helps AI understand content structure
- Article schema with author, dates, engagement metrics
- BreadcrumbList for navigation context
- Clean, semantic HTML structure
- Proper heading hierarchy
- Internal links preserved for context
All SEO-related URLs use the canonical domain https://www.getwewrite.app. Configuration is centralized in app/utils/urlConfig.ts:
PRODUCTION_URL:'https://www.getwewrite.app'getBaseUrl(): Returns the appropriate base URL for the current environmentgetFullUrl(path): Combines base URL with a path
Fallback URLs in SEO files should always use https://www.getwewrite.app (with www) for consistency.
Last updated: January 5, 2026