Skip to content

Commit 060abf5

Browse files
committed
feat: add UTM params to superdoc.dev links for traffic attribution
1 parent e2e1d61 commit 060abf5

4 files changed

Lines changed: 111 additions & 4 deletions

File tree

apps/web/src/components/Footer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ export function Footer() {
22
return (
33
<footer className="border-t border-[var(--color-border)] px-6 py-8 text-center text-sm text-[var(--color-text-muted)]">
44
Built by 🦋{" "}
5-
<a href="https://superdoc.dev" className="text-[var(--color-accent)] hover:underline">
5+
<a
6+
href="https://superdoc.dev/?utm_source=ooxml.dev&utm_medium=referral&utm_campaign=footer"
7+
className="text-[var(--color-accent)] hover:underline"
8+
>
69
SuperDoc
710
</a>
811
</footer>

apps/web/src/data/docs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const docs: Record<string, DocPage> = {
3333
{ type: "heading", level: 2, text: "What's Different Here" },
3434
{
3535
type: "paragraph",
36-
text: "Unlike the official spec, this reference shows live previews, includes real implementation notes from building [SuperDoc](https://superdoc.dev), and links to 100k+ real documents from [docx-corpus](https://docxcorp.us).",
36+
text: "Unlike the official spec, this reference shows live previews, includes real implementation notes from building [SuperDoc](https://superdoc.dev/?utm_source=ooxml.dev&utm_medium=referral&utm_campaign=docs), and links to 100k+ real documents from [docx-corpus](https://docxcorp.us).",
3737
},
3838
{ type: "heading", level: 2, text: "OOXML Structure" },
3939
{
@@ -506,7 +506,7 @@ Each border element has:
506506
content: [
507507
{
508508
type: "paragraph",
509-
text: "Lessons learned from building [SuperDoc](https://superdoc.dev).",
509+
text: "Lessons learned from building [SuperDoc](https://superdoc.dev/?utm_source=ooxml.dev&utm_medium=referral&utm_campaign=docs).",
510510
},
511511
{ type: "heading", level: 2, text: "Word-Specific Issues" },
512512
{

apps/web/src/pages/Mcp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export function Mcp() {
228228
<span className="text-sm text-[var(--color-text-muted)]">
229229
Built by 🦋{" "}
230230
<a
231-
href="https://superdoc.dev"
231+
href="https://superdoc.dev/?utm_source=ooxml.dev&utm_medium=referral&utm_campaign=mcp-page"
232232
target="_blank"
233233
rel="noopener noreferrer"
234234
className="text-[var(--color-accent)] hover:underline"

content-strategy.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# SEO Improvement Plan for ooxml.dev
2+
3+
## Context
4+
5+
ooxml.dev gets ~270 sessions/quarter with only 40 from organic search. But organic visitors have excellent engagement (3+ min avg, 12% bounce) — proving the content is valuable. The problem is zero `/docs/*` pages get organic traffic because Google can't index a client-side SPA. All doc content is static (defined in `docs.ts`), making pre-rendering straightforward.
6+
7+
## Approach: Build-time pre-rendering + SEO metadata
8+
9+
Use `react-dom/server`'s `renderToString` in a custom build script to generate static HTML for each route. No framework migration, no new runtime dependencies.
10+
11+
## Implementation
12+
13+
### 1. Pre-render script (`apps/web/scripts/prerender.tsx`)
14+
15+
Runs after `vite build`:
16+
1. Reads `dist/index.html` as template (has hashed CSS/JS from Vite)
17+
2. For each route, renders the page component to HTML via `renderToString`
18+
3. Injects per-page `<title>`, `<meta>`, OG tags, canonical URL, JSON-LD into `<head>`
19+
4. Writes to correct path (e.g., `dist/docs/tables/index.html`)
20+
5. Client-side React still loads and takes over for interactivity
21+
22+
**Routes to pre-render:**
23+
- `/` (Home)
24+
- `/mcp` (MCP page)
25+
- `/spec` (shell only — content is API-dependent)
26+
- `/docs` + all 7 doc pages from `docs.ts`
27+
28+
**SuperDocPreview handling:** Add `typeof window === 'undefined'` guard to render the XML as a static `<pre><code>` block during SSR. React hydrates the interactive version client-side.
29+
30+
### 2. SEO metadata (`apps/web/src/data/seo.ts`)
31+
32+
Per-route metadata:
33+
- `<title>` — e.g., "OOXML Tables (w:tbl) — Structure & Implementation | ooxml.dev"
34+
- `<meta name="description">` — from `docs.ts` descriptions
35+
- `<link rel="canonical">``https://ooxml.dev{path}`
36+
- Open Graph tags (og:title, og:description, og:url, og:type)
37+
- Twitter card meta
38+
39+
For doc pages, auto-generate from `docs.ts` (title, badge, description).
40+
41+
### 3. Client-side title updates (`useDocumentTitle` hook)
42+
43+
Simple `useEffect` hook so browser tab title updates during SPA navigation. Used in DocsPage, Home, Mcp, SpecExplorer.
44+
45+
### 4. Structured data (JSON-LD)
46+
47+
Injected by prerender script per page:
48+
- Doc pages: `TechArticle` schema
49+
- Home: `WebSite` schema with `SearchAction` for `/spec?q=`
50+
51+
### 5. Sitemap generation
52+
53+
Generated by prerender script → `dist/sitemap.xml`:
54+
- All routes with `<loc>`, `<changefreq>`, `<priority>`
55+
- Auto-includes new doc pages from `docs.ts`
56+
57+
### 6. robots.txt (`apps/web/public/robots.txt`)
58+
59+
```
60+
User-agent: *
61+
Allow: /
62+
Sitemap: https://ooxml.dev/sitemap.xml
63+
```
64+
Plus existing AI crawler blocks.
65+
66+
### 7. Build script update
67+
68+
```diff
69+
- "build": "tsc && vite build"
70+
+ "build": "tsc && vite build && bun scripts/prerender.tsx"
71+
```
72+
73+
## Files to create/modify
74+
75+
| File | Action |
76+
|------|--------|
77+
| `apps/web/scripts/prerender.tsx` | **Create** — core prerender + sitemap generation |
78+
| `apps/web/src/data/seo.ts` | **Create** — per-route SEO metadata |
79+
| `apps/web/src/hooks/useDocumentTitle.ts` | **Create** — client-side title hook |
80+
| `apps/web/src/components/SuperDocPreview.tsx` | **Modify** — add SSR fallback |
81+
| `apps/web/src/pages/docs/Page.tsx` | **Modify** — use useDocumentTitle |
82+
| `apps/web/src/pages/Home.tsx` | **Modify** — use useDocumentTitle |
83+
| `apps/web/src/pages/Mcp.tsx` | **Modify** — use useDocumentTitle |
84+
| `apps/web/src/pages/SpecExplorer.tsx` | **Modify** — use useDocumentTitle |
85+
| `apps/web/public/robots.txt` | **Create** |
86+
| `apps/web/package.json` | **Modify** — update build script |
87+
88+
## Verification
89+
90+
1. `bun run build` from `apps/web/`
91+
2. Inspect `dist/docs/tables/index.html` — should contain full HTML content, correct `<title>`, meta tags, JSON-LD
92+
3. Inspect `dist/sitemap.xml` — should list all routes
93+
4. Serve `dist/` locally (`bunx serve dist`) and verify:
94+
- Pages load with correct content before JS executes (disable JS in browser)
95+
- Interactive features (SuperDocPreview, spec search) work after JS loads
96+
- SPA navigation updates browser tab title
97+
5. Deploy and submit sitemap to Google Search Console
98+
99+
## Post-deploy
100+
101+
- Set up Google Search Console if not already done
102+
- Submit sitemap
103+
- Request indexing for key doc pages
104+
- Monitor indexing progress over 2-4 weeks

0 commit comments

Comments
 (0)