Your site is called "Vite App" in Google. Fix it in one build step.
Headlark reads every HTML file your framework just built and writes the title, meta description, Open Graph and X cards, canonical, JSON-LD and missing alt text into it, before your host uploads it. Every crawler sees real tags, including the link previewers that never run JavaScript.
Free for one site and 25 pages. No credit card needed for the free plan. Or check your live site first, no account needed.
Two lines. Then forget it exists.
npm i -D headlark
# package.json
"scripts": {
"build": "vite build && headlark dist --site-url https://example.com"
}
- Set
HEADLARK_KEYto your site's secret key in the build environment (Vercel, Netlify, GitHub Actions, any CI). - Point it at your output folder. Vite and Astro build to
dist; Next.js withoutput: "export"builds toout. - On Astro,
npx astro add astro-headlarkadds the integration instead. It takes the address fromsitein your config. - On Netlify, use the build plugin instead: one block in
netlify.toml. - Add
--strictif a page that can't be optimized should fail the build. Without it, that page is left untouched.
Tags in the HTML, not added by a script later.
Facebook, LinkedIn, Slack and X read the raw HTML when they build a link preview. Tags a script adds in the browser are invisible to them. Headlark rewrites the files themselves, so what you deploy is what every crawler reads.
Rebuilds are free
Unchanged pages come from cache. Build timestamps, counters and other incidental changes don't count as content, so deploying ten times a day costs nothing.
Idempotent
Running it twice on the same output changes nothing. Headlark undoes its own tags before it applies new ones, so rebuilds converge.
Your choices win
An existing canonical, robots directive or JSON-LD type is kept. Headlark replaces generic titles and descriptions and fills in what's missing.
Everything in the head, grounded in the page.
| Tag | What you get |
|---|---|
| <title> | 30 to 60 characters, topic first, brand last. |
| meta description | A plain sentence of 70 to 155 characters about what the visitor gets. |
| Open Graph, X card | og:title, og:description, og:url, og:type and og:image (an image already on your page), plus twitter:card and friends. |
| JSON-LD | One to three schema.org objects that fit the content, such as SoftwareApplication, Article, Product or LocalBusiness, with only values found on the page. |
| canonical, robots | Added only when missing. Error pages get noindex. |
| alt text | For images that have no alt, written from the page's context. |
| <html lang> | Added when missing. Everything is written in the page's own language. |
Prices, phone numbers, dates and emails in the output are checked against the page before they ship.
Where it doesn't fit.
- Server-rendered pages (SSR, ISR) have no HTML at build time. Prerender them, or use the deploy hook with the snippet (Google only).
- Client-only SPAs give an empty shell. Prerender at build time first, then add the build step.
- It doesn't create images. og:image points at the page's existing og:image or its most representative image.
Ship your next deploy with SEO.
Free tools to see where you stand: the site checker, SEO checker, structured data checker and llms.txt generator.
Guides for Vite, Astro, Next.js static export and Netlify.
Start free →