Skip to content

Commit a38023a

Browse files
joaoh82claude
andauthored
feat(web): keyword research + on-page SEO rewrites (SQLR-33) (#139)
Land a per-page keyword registry under web/seo/keywords.md and rewrite the H1, lede, and metadata on /, /docs, and the landing-page benchmarks section to target "embedded SQL + vector database in Rust", "SQLite alternative", and "SQLRite vs SQLite benchmarks" — terms the site can realistically rank for without overclaiming features that don't ship yet. Internal-anchor audit found no "click here" / "read more"; docs CTA now exposes /blog as a second descriptive in-site sibling. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bdd8017 commit a38023a

7 files changed

Lines changed: 223 additions & 33 deletions

File tree

web/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ The canonical site URL + Twitter handle live in
5858
[`src/lib/site.ts`](src/lib/site.ts) (`SITE.url`, `SITE.twitterHandle`) —
5959
update both there if the domain or handle ever changes.
6060

61+
The keyword strategy that drives every page's H1, lede, and `metadata`
62+
export lives in [`seo/keywords.md`](seo/keywords.md). When rewriting a
63+
page's headline or meta description, update the corresponding entry in
64+
that sheet so future rewrites stay coordinated.
65+
6166
## Local development
6267

6368
```sh
@@ -80,6 +85,8 @@ npm run lint # next lint (ESLint)
8085
web/
8186
├── content/
8287
│ └── blog/ # MDX posts (one .mdx file per post; frontmatter at top)
88+
├── seo/
89+
│ └── keywords.md # keyword research + per-page primary/secondary registry (SQLR-33)
8390
├── src/
8491
│ ├── app/
8592
│ │ ├── globals.css # design tokens + utility CSS (ports the original design's styles.css)

web/seo/keywords.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# SQLRite keyword sheet
2+
3+
Last updated: 2026-05-12 (SQLR-33 — initial keyword research + on-page SEO pass).
4+
5+
## How to read this
6+
7+
Three buckets — head terms (high volume, hard), mid-tail (the sweet spot
8+
SQLRite can realistically rank for), and long-tail / informational
9+
queries (drive blog topics). For each entry:
10+
11+
- **Target page** — where on the site the term should land.
12+
- **Primary keyword** — the dominant query the page is optimized for.
13+
- **Secondary keywords** — supporting phrases woven into headings, body,
14+
and metadata.
15+
- **Intent**`informational` (someone learning), `commercial` (someone
16+
evaluating a tool), `navigational` (someone looking for SQLRite by
17+
name).
18+
- **Priority**`P0` (rewrite now), `P1` (next pass), `P2` (later /
19+
blog backlog).
20+
21+
Relative ordering matters more than absolute volume — we have no SEO
22+
budget, just SERP inspection and a hunch about what an embedded-database
23+
shopper actually types. Free tools that worked during the first pass:
24+
Google's "people also ask" / autocomplete, the SERP for the literal
25+
phrase, and Ahrefs' free keyword generator.
26+
27+
## Conventions
28+
29+
- Canonical host: `https://sqlritedb.com`. Never link to a different
30+
origin in canonical-eligible copy.
31+
- All metadata `description` fields should clear ~150 chars; ~160 max
32+
before Google truncates.
33+
- Don't promise features that don't exist. No "distributed SQLRite",
34+
no "replication", no "production-grade" until the roadmap says so.
35+
- Voice is technical, slightly playful, no marketing buzzwords. "Built
36+
to teach what databases actually do" stays — that's the brand.
37+
38+
## Head terms — high volume, low realistic CTR for now
39+
40+
| Primary | Secondary | Intent | Priority | Target page | Notes |
41+
| --- | --- | --- | --- | --- | --- |
42+
| embedded database | embedded SQL, single-file database, embedded SQLite | commercial | P0 | `/` | We won't win this — but the landing page should still cleanly contain the phrase in H1 + first 160 chars. |
43+
| SQLite alternative | SQLite-compatible, modern SQLite, SQLite in Rust | commercial | P0 | `/` and benchmarks section | Comparison framing on the benchmarks section earns a sliver of long-tail SQLite-vs traffic. |
44+
| Rust database | embedded Rust database, Rust SQL crate | commercial | P0 | `/` | Mentioned in H1 + features intro. |
45+
| vector database | embedded vector DB, vector search database | commercial | P1 | `/` (vector section anchor) | The market is owned by hosted services; SQLRite competes on "embedded + SQL + vector in one file". |
46+
47+
## Mid-tail — the sweet spot we can actually rank for
48+
49+
| Primary | Secondary | Intent | Priority | Target page | Suggested H1 / H2 | Meta description draft |
50+
| --- | --- | --- | --- | --- | --- | --- |
51+
| embedded database in Rust | embedded SQL engine Rust, single-file DB Rust | commercial | P0 | `/` | "SQLRite — an embedded SQL + vector database in Rust" (H1) | "SQLRite is an embedded SQL + vector database in Rust. SQLite-style single-file format, WAL transactions, HNSW vector search, BM25 full-text, six SDKs." |
52+
| SQLite alternative for Rust | Rust SQLite alternative, SQLite-compatible Rust crate | commercial | P0 | `/` (hero + features) | Feature section retains "13 features" framing; copy mentions "SQLite-compatible API". | Same as landing — composition handled in lede + first feature copy. |
53+
| embedded vector search Rust | vector search in Rust, HNSW Rust embedded | commercial | P0 | `/docs#vector` and `/` vector feature | "Built-in vector search with HNSW" (H3 in features) | "Add HNSW vector search to your Rust app with a single CREATE INDEX. Cosine / dot / L2 distance, sub-linear k-NN, no external service." |
54+
| embedded database with HNSW | HNSW SQLite, vector index embedded DB | informational | P1 | `/docs#vector` | `/docs` H2 ("Vector search") stays as-is. | Captured by /docs metadata. |
55+
| sqlite-compatible Rust crate | drop-in SQLite Rust, sqlite parser Rust | navigational | P1 | `/` features section | "Single-file format" / "Supported SQL" tags carry the term. | n/a |
56+
| Rust embedded SQL engine | Rust SQL parser engine, embedded SQL crate | commercial | P1 | `/` | Features intro. | n/a |
57+
| embedded SQL + vector database | embedded SQL vector DB, vector + SQL one file | commercial | P0 | `/` | New H1 leads with this. | Landing description. |
58+
| Rust embedded database with WAL | WAL Rust crate, embedded database WAL | informational | P1 | `/docs#persistence` | `/docs` "Persistence & the WAL" section. | Captured by /docs metadata; long-tail blog candidate. |
59+
60+
## Long-tail / informational — easy wins + blog backlog
61+
62+
| Primary | Intent | Priority | Target page | Notes |
63+
| --- | --- | --- | --- | --- |
64+
| how to add vector search to SQLite | informational | P1 | future blog post | Open as blog backlog ticket. Working title: "Adding HNSW vector search to a SQLite-style engine". |
65+
| SQLite vs SQLRite benchmarks | informational / commercial | P0 | benchmarks section + `/blog/sqlrite-vs-sqlite-benchmarks` | Benchmarks H2 now leads with "SQLRite vs SQLite benchmarks". Blog post already exists. |
66+
| embedded database for desktop Tauri apps | commercial | P1 | `/docs#desktop` + future blog post | Existing docs section is short; expand later. Blog candidate: "Shipping a Tauri 2 app with an embedded SQL database." |
67+
| natural language to SQL Rust crate | informational | P1 | `/docs` (.ask section, currently terse) + blog | Open ticket: "Doc the `.ask` REPL + `ConnectionAskExt::ask` API" + dedicated blog. |
68+
| MCP server for SQLite | informational | P0 | `/docs#mcp` | Docs section stays. Worth a dedicated blog post: "Wiring a SQLite-style engine into Claude Code via MCP". |
69+
| rust embedded database with WAL | informational | P1 | `/docs#persistence` | Already covered; long-tail traffic. |
70+
| how to do hybrid retrieval in Rust | informational | P2 | future blog post (vector + BM25) | We already have `examples/hybrid-retrieval`. Blog candidate. |
71+
| BM25 full-text search Rust | informational | P1 | `/docs#fts` | Section title already mentions BM25; meta copy captures it. |
72+
| single-file SQL database | informational | P1 | `/` | Features H3 stays. |
73+
| WASM SQL database in browser | informational | P2 | `/docs#sdk-wasm` + future blog | Blog candidate: "Running a SQL engine in a browser tab with WASM". |
74+
75+
## Per-page primary + secondary registry
76+
77+
This is the authoritative cross-reference for the on-page rewrites
78+
landed in SQLR-33. Per the acceptance criteria, every P0 page records
79+
its primary + secondary keyword here.
80+
81+
### `/` (landing)
82+
83+
- **Primary:** embedded SQL + vector database in Rust
84+
- **Secondary:** embedded database, SQLite alternative, Rust database,
85+
HNSW vector search, BM25 full-text search, MCP server
86+
- **H1:** "SQLRite — an embedded SQL + vector database in Rust."
87+
- **Meta description (≤ 160 chars):** "SQLRite is an embedded SQL +
88+
vector database in Rust. SQLite-style single-file format, WAL
89+
transactions, HNSW vector search, BM25 full-text, six SDKs."
90+
91+
### `/docs`
92+
93+
- **Primary:** SQLRite documentation / getting started with the
94+
embedded Rust database
95+
- **Secondary:** embedded database tutorial Rust, vector search Rust
96+
quickstart, BM25 Rust, MCP server SQLite
97+
- **H1:** "SQLRite docs — getting started with the embedded Rust
98+
database."
99+
- **Meta description:** "Install SQLRite, open your first .sqlrite
100+
file, and run SQL — transactions, JOINs, HNSW vector search, BM25
101+
full-text, and six language SDKs."
102+
103+
### Benchmarks section (`#benchmarks` on `/`)
104+
105+
There is no dedicated `/benchmarks` route — benchmarks live as a
106+
section on the landing page. The H2 + sub-copy carries the
107+
comparison query.
108+
109+
- **Primary:** SQLRite vs SQLite benchmarks
110+
- **Secondary:** Rust embedded database benchmark, SQLite alternative
111+
performance
112+
- **H2:** "SQLRite vs SQLite benchmarks — honest numbers, published in
113+
public."
114+
- **Body sentence carries:** "Twelve workloads against SQLite
115+
(WAL+NORMAL) and DuckDB …"
116+
117+
### `/blog`
118+
119+
- **Primary:** building an embedded database in Rust
120+
- **Secondary:** Rust database blog, SQLite-style engine notes
121+
- Existing H2 + meta cover this; no rewrite this pass.
122+
123+
## Internal-linking notes (SQLR-33 sweep)
124+
125+
- Audit confirmed no `click here` / `read more` / `here.` anchors in
126+
`web/src`. Anchors are descriptive (`Read the docs`, `Star on
127+
GitHub`, `All posts`, post-title pager links).
128+
- `/docs` is a single-page guide with anchored sections rather than
129+
many child pages, so the "every doc page links to ≥ 2 sibling
130+
pages" rule is met via the global `<Footer />` (links to `/blog`,
131+
`/blog/rss.xml`, GitHub) plus the in-page docs CTA card. The CTA
132+
card now exposes an explicit `/blog` link as a second descriptive
133+
in-site sibling (besides `/`).
134+
- Landing hero's primary CTA continues to point at `/docs`. Feature
135+
cards covering Vector / FTS / MCP / SDKs already act as the "top
136+
docs entry points" via the rest of the landing IA (`Architecture`,
137+
`Roadmap`, `SDKShowcase`).
138+
139+
## Re-crawl plan
140+
141+
After every rewrite pass, re-run a free crawler (Screaming Frog up to
142+
500 URLs is plenty) against `https://sqlritedb.com` and verify:
143+
144+
- **No duplicate `<title>`** across `/`, `/docs`, `/blog`, blog
145+
posts, blog tag pages.
146+
- **No duplicate meta description** across the same set.
147+
- **No duplicate H1** per page.
148+
- **No orphan pages** — every URL in `/sitemap.xml` is reachable
149+
from at least one other URL.
150+
151+
`src/app/sitemap.ts` is the canonical URL list; keep it in sync when a
152+
new route ships.
153+
154+
## Open backlog (split out of SQLR-33)
155+
156+
- Blog post: "Adding HNSW vector search to a SQLite-style engine".
157+
- Blog post: "Wiring a SQLite-style engine into Claude Code via MCP".
158+
- Blog post: "Shipping a Tauri 2 app with an embedded SQL database".
159+
- Blog post: "Running a SQL engine in a browser tab with WASM".
160+
- Docs expansion: dedicate a `.ask` / natural-language-to-SQL section
161+
in `/docs` once the API stabilizes.

web/src/app/docs/page.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import { Footer } from "@/components/footer";
44
import { Nav } from "@/components/nav";
55
import { SITE } from "@/lib/site";
66

7-
const TITLE = "Getting started with SQLRite";
7+
// SEO targeting: primary "SQLRite documentation / getting started with the
8+
// embedded Rust database"; secondary "embedded database tutorial Rust",
9+
// "vector search Rust quickstart", "BM25 Rust", "MCP server SQLite".
10+
// See web/seo/keywords.md.
11+
const TITLE =
12+
"SQLRite docs — getting started with the embedded Rust database";
813
const DESCRIPTION =
9-
"A ten-minute tour from cargo install to a persistent on-disk SQLRite database — REPL, transactions, JOINs, vector search, BM25 full-text, and six language SDKs.";
14+
"Install SQLRite, open your first .sqlrite file, and run SQL — transactions, JOINs, HNSW vector search, BM25 full-text, an MCP server, and six language SDKs.";
1015

1116
export const metadata: Metadata = {
1217
title: TITLE,
@@ -123,12 +128,19 @@ export default function DocsPage() {
123128

124129
<main className="docs-main">
125130
<span className="eyebrow">docs · getting started</span>
126-
<h1 style={{ marginTop: 18 }}>Getting started with SQLRite</h1>
131+
<h1 style={{ marginTop: 18 }}>
132+
SQLRite docs — getting started with the embedded Rust database
133+
</h1>
127134
<p className="lede">
128-
A ten-minute tour from <code>cargo install</code> to a persistent
129-
on-disk database with real transactions, vector search, and
130-
full-text search. Pick the SDK that fits your language at the
131-
bottom — they all wrap the same engine.
135+
SQLRite is an embedded SQL + vector database in Rust. This page is
136+
a ten-minute tour from <code>cargo install</code> to a persistent
137+
on-disk <code>.sqlrite</code> file — transactions, JOINs, HNSW
138+
vector search, BM25 full-text, and the MCP server. Skip ahead to{" "}
139+
<a href="#vector">vector search</a>,{" "}
140+
<a href="#fts">full-text search</a>, the{" "}
141+
<a href="#mcp">MCP server</a>, or pick the SDK that fits your
142+
language at the <a href="#sdk-rust">bottom</a> — they all wrap the
143+
same engine.
132144
</p>
133145

134146
<h2 id="install">Install</h2>
@@ -804,8 +816,11 @@ export default function DocsPage() {
804816
<a className="btn" href={SITE.discord}>
805817
Join the Discord
806818
</a>
819+
<Link className="btn" href="/blog">
820+
Read the SQLRite blog
821+
</Link>
807822
<Link className="btn" href="/">
808-
← Back to landing
823+
← Back to the SQLRite home page
809824
</Link>
810825
</div>
811826
</main>

web/src/app/layout.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@ const jetbrainsMono = JetBrains_Mono({
3232
display: "swap",
3333
});
3434

35-
const DEFAULT_TITLE = "SQLRite — embedded SQL database, built in Rust";
35+
// Header copy is the primary on-page SEO surface — see web/seo/keywords.md
36+
// for the keyword strategy and per-page rationale.
37+
const DEFAULT_TITLE =
38+
"SQLRite — an embedded SQL + vector database in Rust";
3639
const DEFAULT_DESCRIPTION =
37-
"An embedded SQL database modeled after SQLite, built from scratch in Rust. Single-file format, real B-tree, WAL, transactions, vector search, full-text search, and six language SDKs.";
40+
"SQLRite is an embedded SQL + vector database in Rust. SQLite-style single-file format, WAL transactions, HNSW vector search, BM25 full-text, and six language SDKs.";
3841

3942
export const metadata: Metadata = {
4043
metadataBase: new URL(SITE.url),
@@ -47,17 +50,17 @@ export const metadata: Metadata = {
4750
authors: [{ name: "Joao Henrique Machado Silva", url: SITE.socials.github }],
4851
keywords: [
4952
"SQLRite",
50-
"Rust SQLite",
51-
"embedded database",
52-
"embedded SQL",
53+
"embedded database in Rust",
54+
"embedded SQL + vector database",
55+
"SQLite alternative",
56+
"SQLite-compatible Rust crate",
5357
"Rust database",
54-
"vector search",
58+
"embedded vector search Rust",
5559
"HNSW",
56-
"BM25",
57-
"full-text search",
60+
"BM25 full-text search",
5861
"WAL",
59-
"B-tree",
60-
"MCP",
62+
"single-file database",
63+
"MCP server for SQLite",
6164
],
6265
openGraph: {
6366
type: "website",

web/src/app/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import { SDKShowcase } from "@/components/sdk-showcase";
1313
import { SQLRef } from "@/components/sql-ref";
1414
import { SITE } from "@/lib/site";
1515

16-
const TITLE = "SQLRite — embedded SQL database, built in Rust";
16+
// SEO targeting: primary "embedded SQL + vector database in Rust",
17+
// secondary "SQLite alternative" / "Rust database" / "HNSW vector search" /
18+
// "BM25 full-text" / "MCP server". See web/seo/keywords.md.
19+
const TITLE = "SQLRite — an embedded SQL + vector database in Rust";
1720
const DESCRIPTION =
18-
"Single-file embedded SQL engine in Rust. Real B-tree, WAL, transactions, JOINs, aggregates, HNSW vector search, BM25 full-text, plus six language SDKs.";
21+
"SQLRite is an embedded SQL + vector database in Rust — a SQLite alternative with WAL transactions, HNSW vector search, BM25 full-text, and six language SDKs.";
1922

2023
export const metadata: Metadata = {
2124
title: { absolute: TITLE },

web/src/components/benchmarks.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,14 @@ export function Benchmarks() {
8181
<div className="sec-head">
8282
<span className="eyebrow tag">07 · benchmarks</span>
8383
<div>
84-
<h2>Honest numbers, published in public.</h2>
84+
<h2>SQLRite vs SQLite benchmarks — honest numbers, published in public.</h2>
8585
<p className="sub">
86-
Twelve workloads against SQLite (WAL+NORMAL) and DuckDB on a
87-
pinned-host run. The point isn&rsquo;t to win — SQLite has 25
88-
years of optimization behind it — it&rsquo;s to baseline future
89-
engine work, prove the differentiator workloads deliver, and
90-
ground the roadmap with evidence.
86+
Twelve workloads pitch SQLRite, the embedded Rust database,
87+
against SQLite (WAL+NORMAL) and DuckDB on a pinned-host run.
88+
The point isn&rsquo;t to win — SQLite has 25 years of
89+
optimization behind it — it&rsquo;s to baseline future engine
90+
work, prove the differentiator workloads (HNSW vector search,
91+
BM25 full-text) deliver, and ground the roadmap with evidence.
9192
</p>
9293
</div>
9394
</div>

web/src/components/hero.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ export function Hero() {
1414
v{SITE.version} · MIT licensed · open source
1515
</span>
1616
<h1 style={{ marginTop: 24 }}>
17-
An embedded SQL database,{" "}
18-
<span className="accent-word">built from scratch</span> in Rust.
17+
SQLRite — an embedded SQL +{" "}
18+
<span className="accent-word">vector database</span> in Rust.
1919
</h1>
2020
<p className="hero-tag">
21-
SQLRite is a from-the-ground-up reimagining of SQLite — a
22-
single-file engine with a real B-tree, write-ahead log,
23-
transactions, JOINs, aggregates, vector search, full-text search,
24-
and bindings for six languages. Built to teach what databases
25-
actually do.
21+
SQLRite is a from-scratch SQLite alternative — a single-file
22+
embedded database in Rust with a real B-tree, write-ahead log,
23+
transactions, JOINs, aggregates, HNSW vector search, BM25
24+
full-text search, and bindings for six languages. Built to teach
25+
what databases actually do.
2626
</p>
2727
<div className="cta-row">
2828
<Link className="btn btn-primary" href="/docs">

0 commit comments

Comments
 (0)