Skip to content
imarch.dev
Back to blog
· 3 min read

A Blog in One Evening

astro seo claude code devops

Friday evening. The site exists, the content exists - eight LinkedIn posts about Planq. But no blog. Search engines see a landing page with three pages and have no idea why they should show it to anyone. Google Search Console is connected, Yandex Webmaster is connected, but there’s nothing to index.

Decided to fix that. In one evening.

Clock showing late hours with blog post cards flying around

Stack

The site runs on Astro 5, Tailwind, Cloudflare Pages. Three locales - Russian by default, English and Kazakh with /en/ and /kk/ prefixes. Static generation, no server. Everything through content collections and getStaticPaths().

For the blog I needed:

  • A content collection with a schema (title, description, locale, pubDate, tags, keywords)
  • List and article pages for each locale (6 files)
  • Card and post components
  • SEO on every page - JSON-LD BlogPosting, og:type=article, meta keywords
  • Header navigation
  • Sitemap

Eight posts in an hour

The content was already there. Seven posts from LinkedIn about Planq and one about chatbot security. Wrapped them in markdown with frontmatter, cleaned up the LinkedIn formatting, replaced em-dashes with normal hyphens, removed colons from headings. Eight files in src/content/blog/.

Astro picked them up automatically. getCollection('blog', e => e.data.locale === 'ru') - done. Each article got its own slug from the filename, its own tags and keywords.

OG images on the fly

When you share a link on LinkedIn or Telegram, you want a nice card with the title to appear, not a bare URL. That’s what Open Graph images are for. You could design eight images in Figma. Or you could generate them at build time.

Satori by Vercel takes JSX-like markup and turns it into SVG. @resvg/resvg-js renders SVG to PNG. The result is an endpoint src/pages/og/[slug].png.ts - at build time Astro generates a PNG for each article. Dark background, title, description, tags at the bottom. 1200 by 630 pixels, as social platforms require.

Eight images generate in two seconds. The first one takes a bit longer - loading the font. The rest are about 120 ms each.

Sharing buttons

Each article got three buttons - LinkedIn, Threads, Telegram. Each one builds the right URL with text and link. LinkedIn via sharing/share-offsite, Threads via intent/post, Telegram via t.me/share/url. Icons from Lucide, Threads is a custom SVG.

SEO

Every article gets:

  • <title> and <meta description> from frontmatter
  • <meta keywords> for search engines
  • og:type=article instead of the default website
  • og:image pointing to the generated PNG
  • JSON-LD BlogPosting with author, date, keywords
  • JSON-LD BreadcrumbList for navigation
  • Canonical URL and hreflang for all three locales

Updated the sitemap manually - it’s static in public/sitemap.xml. Nine new URLs.

The chatbot knows too

The site has an AI chatbot - a virtual clone that answers visitor questions. Updated its system prompt - now it knows about all the blog posts and can recommend them in conversation. “By the way, Ilyas has a post about this” - casual, on topic.

The bottom line

Nine posts. Six pages. Two components. Eight OG images. Three sharing buttons. Navigation. SEO. Sitemap. Updated chatbot.

One evening. One terminal. Claude Code alongside as a partner - reading code, suggesting solutions, writing, committing. I review and steer.

Tomorrow Google will index nine new pages instead of three. And I’ll write about it on LinkedIn - short, with a link to the blog. Full circle.

Share:

Related posts