seo(landing): query-driven titles, H1s and descriptions for the framework pages, home and top storage guides - #417
Open
BSalaeddin wants to merge 1 commit into
Open
seo(landing): query-driven titles, H1s and descriptions for the framework pages, home and top storage guides#417BSalaeddin wants to merge 1 commit into
BSalaeddin wants to merge 1 commit into
Conversation
…work pages, home and top storage guides
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Search Console, 90 days to 2026-09-08 (
sc-domain:useupup.com): 27 clicks on5,812 impressions. The impressions are already there; the titles and
descriptions that have to convert them are not.
/(+ queryupup)<title>77 chars — truncated in SERPs; description 262 chars/vue//react//angular/angular-file-uploader/angular file uploader/file uploader angular/svelte//vanilla//docs/guides/storage/azure-blob/upload files to blob storage,azure upload blob with sas url put blob headers— the title said none of it/docs/guides/storage/digitalocean-spaces//docs/comparisons/upup-vs-uploadthing/uploadthing10 impr @38/docs/guides/storage/cloudflare-r2//docs/guides/server-mode-setup//docs/guides/storage/minio//docs/resumable-uploads//docs/comparisons/upup-vs-uppy//docs/api-reference/events//docs/A docs crawl also flagged 7 descriptions over 170 chars; the real census found
24 of 64 docs pages with a description Google would cut off.
vue file manager(58 impr @65) was deliberately not targeted — upup is nota file manager, so chasing it would be a false claim.
What changed
Titles are ≤ 60 chars with the brand last on non-home pages and first on
home; descriptions are 120–155 and carry the phrasing searchers actually type
(
file uploader,drag and drop,free,open source,npm install @useupup/<fw>,resumable,S3). No invented claims — no user counts, no"most popular", no "#1"; "free and open-source (MIT)" and "six frameworks" are
both true.
byte-identicalis not reintroduced anywhere (a separate PR retiresthat claim).
Home
<title>upup – One File Uploader for React, Vue, Svelte, Angular, Vanilla JS & Preact(77)upup – Open-Source File Uploader for Every Framework(52)One open-source, MIT-licensed file uploader with a headless core and byte-identical native UI for six frameworks. Drag-and-drop, cloud drives (Google Drive, OneDrive, Dropbox, Box), camera, screen capture, and secure server-mode uploads to S3-compatible storage.(262)upup is a free, open-source drag and drop file uploader with native UI for React, Vue, Svelte, Angular, Vanilla JS and Preact. MIT-licensed.(140)<h1>One File Uploader, Every FrameworkOpen-Source File Uploader for Every Framework(6 words)siteConfig.title(site-wide fallback)upup – One File Uploader for Every Framework | Open Source(58)upup – Open-Source File Uploader for Every Framework(52)siteConfig.tagline(site-wide fallback)Free and open-source (MIT) drag and drop file uploader for React, Vue, Svelte, Angular, Vanilla JS and Preact, with resumable uploads to S3.(140)Framework pages
<title>template went from<Name> File Uploader — upupto<Name> File Uploader – Open-Source Drag & Drop | upup; the description is nowthe per-framework
taglineinsrc/lib/frameworks.tsx(it had no otherconsumer). The H1's two-line structure is unchanged — the
BlurTextline nowcarries
<Name> File Uploaderand the gradient line carriesOne core, every framework.<title>(len)<h1>/react/React File Uploader – Open-Source Drag & Drop | upup(52)React File Uploader/One core, every frameworkFree, open-source React file uploader with drag and drop, cloud drives, an image editor and resumable S3 uploads — npm install @useupup/react.(142)/vue/Vue File Uploader – Open-Source Drag & Drop | upup(50)Vue File Uploader/ …Free, open-source Vue file uploader with drag and drop, cloud drives and resumable uploads to any S3 storage — npm install @useupup/vue.(136)/svelte/Svelte File Uploader – Open-Source Drag & Drop | upup(53)Svelte File Uploader/ …Free, open-source Svelte file uploader with drag and drop, cloud drives and resumable uploads to any S3 storage — npm install @useupup/svelte.(142)/angular/Angular File Uploader – Open-Source Drag & Drop | upup(54)Angular File Uploader/ …Free, open-source Angular file uploader with drag and drop, cloud drives and resumable uploads to any S3 storage — npm install @useupup/angular.(144)/vanilla/Vanilla JS File Uploader – Open-Source Drag & Drop | upup(57)Vanilla JS File Uploader/ …Free, open-source file uploader for plain JavaScript and TypeScript — drag and drop, cloud drives, resumable S3 uploads. npm install @useupup/vanilla.(150)/preact/Preact File Uploader – Open-Source Drag & Drop | upup(53)Preact File Uploader/ …Free, open-source Preact file uploader with drag and drop, cloud drives, an image editor and resumable S3 uploads — npm install @useupup/preact.(144)Each framework page also gained a real internal link to its own quickstart
(
/docs/quickstarts/<id>/, trailing slash) in the hero CTA row — all six slugsexist under
content/docs/quickstarts/. It renders only whenfwis set, sothe home page is unchanged. It is a plain
<Link>with no motion wrapper,deliberately, so it does not collide with
seo/mobile-performance, whichrewrites the hero's animation code in the same file. The rest of my hero edit is
three text-only hunks.
Docs frontmatter
Every listed page was retitled to the query language, plus the 24 descriptions
that were over 160 chars (the budget test would otherwise fail on them, and the
brief says fix rather than loosen).
Lengths below are
titlechars (add 12 for the| upup docstemplate) →descriptionchars.titlebefore → afterguides/storage/azure-blobUpload files to Azure Blob Storage→Upload Files to Azure Blob Storage with a SAS URLguides/storage/digitalocean-spacesUpload files to DigitalOcean Spaces→Upload Files to DigitalOcean Spaces from the Browserguides/storage/cloudflare-r2Upload files to Cloudflare R2→Upload Files to Cloudflare R2 from the Browserguides/storage/minioUpload files to MinIO→Upload Files to MinIO from the Browserguides/server-mode-setupServer Mode — Setup→Server Mode Setup — Upload Through Your Serverresumable-uploadsResumable Uploads→Resumable Uploads — S3 Multipart & tuscomparisons/upup-vs-uploadthingupup vs UploadThing→upup vs UploadThing — Self-Hosted Alternativecomparisons/upup-vs-uppyupup vs Uppy→upup vs Uppy — Which Uploader to Chooseapi-reference/eventsEvents→File Upload Eventsindex(/docs/)upup Documentation→File Uploader Documentationquickstarts/*Add a file uploader to a <fw> app with @useupup/<fw>The azure-blob description now carries the exact query shape:
a server-issued SAS URL, the mandatory
x-ms-blob-typePUT header, and"no account key in the client".
Colons were kept out of the new titles: an unquoted YAML scalar cannot contain
": ", and the corpus's existing style is the em dash. All 64 pages still parseand the page count is still 64 (
docs-source.test.ts/docs-llms.test.tspins untouched).
What CI covers
New
apps/landing/src/__tests__/seo-copy-budgets.test.ts— the budgets stopbeing a convention and start being a gate:
<title>≤ 60, starts with the brand; description 120–160siteConfig.title/.tagline(the site-wide fallback metadata) under thesame budgets
generateMetadata(FRAMEWORK_ID)call:title ≤ 60, title contains
<Name> File Uploader, description 120–160,description contains
npm install <pkg>source.getPages(): frontmatter title + the" | upup docs"template ≤ 65; description ≤ 160 where present — both reportthe offending URLs and their lengths rather than a bare boolean
byte-identicalVerification
All run locally from the worktree, verdicts via
rtk proxy+$LASTEXITCODE(the rtk filter has falsified prettier/tsc/knip verdicts before).
turbo run lint typecheck test --filter=@useupup/landing --force@useupup/landinglint ✅, typecheck ✅; the newseo-copy-budgets.test.ts31 tests passed in 49 ms;docs-source✅ (still 64 pages),docs-llms✅,core146 ✅,react74 ✅,server32 ✅,interactive-example18 ✅. See the flake note below.pnpm run test:qualitygit ls-files)pnpm run vocab:checkpnpm run docs:links:checkpnpm run docs:api-sync:checkpnpm run docs:snippets:coverageprettier --checkon every touched.ts/.tsx.mdxis not in lint-staged's globs or the root prettier scope)turbo run build --filter=@useupup/landing --force--project docs(playwright.landing.config.ts)docs page renders chrome and content(asserts the/docs/getting-started/<h1>, whose title I deliberately left alone),copy-page markdown twin(# Getting Started), anddocs home renders hero…Pre-existing flake, not from this PR:
apps/landing/tests/support-route.test.tsfails its first test (
silently accepts a honeypot-filled submission…) on a5 s timeout — that test does the cold
loadRoute()dynamic import and thetransform alone took 30 s on this (heavily loaded) Windows box; the other 13
pass. Its import graph (
@/lib/analytics/*,@/lib/support/*) has zerooverlap with anything this PR touches.
packages/reactandtests/analytics-isolation.test.tsfailed the same way on the first run andwent green on the isolated re-run, per CLAUDE.md's flake protocol.
Served HTML (production build,
next start -p 4468)All six framework pages, from the same server (title length after entity
decoding, and the quickstart link asserted in the HTML):
Owner questions
One core, every frameworkin the gradient. Would you rather it readOpen source, MIT licensedor the framework's own package name?/docs/title.File Uploader Documentation | upup docsreads slightlyredundant but puts the head term in front of the brand;
upup Documentationdid not rank. Happy to revert if you prefer the brand-first form.
api-reference/eventssidebar label changed fromEventstoFile Upload Events— frontmatter title drives the<title>, the on-page<h1>and the sidebar entry, so every retitle in this PR also changes those.Same applies to the storage guides and the two comparisons.
FrameworkStriplinks to/${fw.id}with notrailing slash, so every internal framework link eats a 308 under
trailingSlash: true. One-character fix, but that file is outside this PR'sbrief and
seo/mobile-performanceis in the same neighbourhood — say theword and I'll take it, or leave it for the hygiene PR.
src/lib/faqs.ts.The FAQ block is shared by the home page and all six framework pages, so
per-framework entries would show on every page, and
seo/claim-integrityowns that file right now (it removes the
byte-identicalanswer). Say theword and I will add them in a follow-up.