惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
IT之家
IT之家
C
Check Point Blog
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
M
MIT News - Artificial intelligence
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
F
Fortinet All Blogs
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How we generate code screenshots for socials
Accreditly · 2026-06-18 · via DEV Community

We post a lot of code to X and LinkedIn. For a long time we grabbed those screenshots by hand: snippet open in the editor, crop the window, drop the PNG into the post. It worked, but it never stayed consistent. The theme drifted between posts, the widths were all over the place, the output looked soft on retina screens, and roughly once a month someone shipped a screenshot with the file tree still showing.

So we stopped doing it by hand. These days we generate code screenshots two ways, and which one we reach for depends entirely on whether a human or a machine is making the image. For one-offs we use a browser tool, the Code Screenshot Generator, which turns a snippet into a PNG without going anywhere near the codebase. For anything that needs to happen at publish time, or the same way across fifty snippets, we call a code screenshot API instead. This post covers both, and why we settled on the split.

Why screenshotting the editor does not scale

Grabbing the editor window is fine for one image. The trouble starts when you do it repeatedly.

Every screenshot ends up slightly different. One uses your current editor theme, the next uses whatever you switched to last week. The line gutter is in one, absent from another. The padding is whatever the window happened to be. Crop them by hand and the widths never match, so a thread of three snippets reads as three different posts stitched together. And because you are capturing a real screen, the output is tied to your display, which means it looks crisp on your machine and soft on everyone else's phone.

Carbon and Ray.so solve the prettiness problem, and we used them happily for years. They are genuinely good in-browser tools. The catch is that both are in-browser only, with no API. The moment you want a screenshot generated automatically, at publish time or inside a build step, a hand tool is the wrong shape for the job. You cannot put a button-click in a deploy pipeline.

The quick route: a browser tool for one-offs

When it is a single snippet for a tweet or a slide, and you are away from the code anyway, the fastest path is the online code screenshot tool. Paste the snippet, pick the language, and you get a clean image back.

It highlights any language Prism or highlight.js recognises, which is roughly three hundred of them, so JavaScript, Python, Rust, SQL, Elixir and the long tail are all covered. You pick a theme (One Dark, Dracula, GitHub, Night Owl and a handful of others, each matching its VS Code counterpart closely), toggle the macOS-style window chrome on or off, set the padding, and choose a solid colour or a gradient behind the code. Then you download the PNG or copy the hosted URL straight into a post.

That covers the ad-hoc case. The interesting part is what happens when you want the same output without a person in the loop.

The repeatable route: a code screenshot API

When the image needs to be produced by a machine, at publish time, in CI, or off a CMS hook, we use the Code Screenshot API. It is a ready-made template on HTML to Image (html2img.com): you POST the code and a few styling options as JSON, and you get back a hosted PNG URL. It is the same renderer that sits behind the browser tool, so the screenshots match whichever route produced them. There is no headless Chrome for you to install, patch, or babysit.

You need two things to follow along: an account with an API key, and a snippet to render. Here is the smallest call that does something useful, with curl:

curl -X POST https://app.html2img.com/api/v1/templates/code-screenshot \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "export async function fetchUser(id: string) {\n  const res = await fetch(`/api/users/${id}`);\n\n  if (!res.ok) {\n    throw new Error(`Failed to load user ${id}`);\n  }\n\n  return res.json() as Promise<User>;\n}",
    "language": "typescript",
    "title": "src/lib/users.ts",
    "theme": "atom-one-dark",
    "background": "linear-gradient(135deg, #6366F1 0%, #8B5CF6 50%, #EC4899 100%)",
    "padding": 72,
    "show_window_chrome": "true",
    "show_line_numbers": "false"
  }'

The fields map onto what you would set in the browser tool. code is the snippet, with newlines escaped in the JSON string. language drives the highlighting. title is the filename shown in the window bar. theme, background, and padding are the styling, and the two show_ flags toggle the window chrome and line numbers. Only code is required; leave the rest off and the template falls back to sensible defaults. The full input list, with example values and validation rules, is in the Code Screenshot template reference.

By default the template renders a 1600 by 1000 PNG, which you can override with width and height. A successful call returns JSON:

{
  "success": true,
  "id": "abc123",
  "url": "https://i.html2img.com/abc123.png",
  "credits_remaining": 1234,
  "template": "code-screenshot"
}

The url is a CDN link to the rendered image. That URL is the whole point: you store it, embed it, or attach it to a post.

Wiring it into the publishing flow

In practice you do not run curl, you call this from the code that publishes the post. Here is the Node version wrapped in a small helper that hands back the image URL:

async function codeScreenshot(code, language, title) {
  const res = await fetch('https://app.html2img.com/api/v1/templates/code-screenshot', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.HTML2IMG_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      code,
      language,
      title,
      theme: 'atom-one-dark',
      background: 'linear-gradient(135deg, #6366F1 0%, #8B5CF6 50%, #EC4899 100%)',
      padding: 72,
      show_window_chrome: 'true',
      show_line_numbers: 'false',
    }),
  });

  if (!res.ok) {
    throw new Error(`code-screenshot render failed: ${res.status}`);
  }

  const { url } = await res.json();
  return url;
}

Call it once per snippet when a post is published, save the returned URL alongside the post, and attach that image to the tweet or LinkedIn share. The code is rendered once and served from the CDN forever after, so there is no per-view cost.

We mostly publish from Laravel, and the shape is identical there. The HTTP client does the work:

use Illuminate\Support\Facades\Http;

$url = Http::withHeaders(['X-API-Key' => config('services.html2img.key')])
    ->post('https://app.html2img.com/api/v1/templates/code-screenshot', [
        'code' => $snippet,
        'language' => 'php',
        'title' => 'app/Actions/RenderSnippet.php',
        'theme' => 'atom-one-dark',
        'show_window_chrome' => 'true',
    ])
    ->throw()
    ->json('url');

The other languages follow the same pattern, and the reference has worked examples for Python, Ruby, React and Vue if you want a closer fit to your stack.

Getting the screenshots to actually read on social

A clean render is only half the job. Social feeds re-compress your image and shrink it, so a few habits make the difference between a screenshot people can read and one they scroll past.

Trim the snippet first. Anything past roughly twenty lines shrinks below a readable point size once the renderer scales it to fit, so cut to the part that matters and split a longer example into a sequence of smaller images rather than one tall one nobody can read.

Pick a theme that survives compression. Low-contrast themes like Solarized Light lose detail when X or LinkedIn re-encode the image for the feed. Higher-contrast themes (One Dark, Dracula, GitHub) hold up far better through that round trip, so favour them for anything social.

Keep it sharp. The browser tool has a 2x DPI toggle for retina output. Through the API the default 1600 by 1000 render is already large enough to stay crisp after a feed re-compresses it, and you can push the dimensions higher if you need more headroom.

Cache the URL hard. A rendered snippet's CDN URL does not change unless the code changes, so set long cache headers and only re-render when the snippet itself moves. Re-rendering an unchanged screenshot on every publish is wasted work.

Which one we reach for

The split is simple. One-offs, and anything where you are already away from the editor, go through the code screenshot generator in the browser. Anything repeatable, or that has to happen at publish time, goes through the code screenshot template and its API. Because both run the same renderer, the two never drift apart, which was the original problem we set out to fix.

If you want to go deeper, the Code Screenshot API reference lists every input, including line numbers, diff highlighting by setting the language to diff, and custom themes via a Prism variables block.

How do you handle code screenshots for your posts at the moment, still grabbing them by hand, or have you wired it into publishing? Let me know in the comments.