<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nico Acosta</title>
    <description>The latest articles on DEV Community by Nico Acosta (@nico_acosta_bc7dceb59f65e).</description>
    <link>https://dev.to/nico_acosta_bc7dceb59f65e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3978200%2F8f88872d-ec24-49b7-ab54-afee796bed22.png</url>
      <title>DEV Community: Nico Acosta</title>
      <link>https://dev.to/nico_acosta_bc7dceb59f65e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nico_acosta_bc7dceb59f65e"/>
    <language>en</language>
    <item>
      <title>How to Screenshot a List of URLs (Bulk Capture for Research and Audits)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Tue, 07 Jul 2026 11:40:47 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-screenshot-a-list-of-urls-bulk-capture-for-research-and-audits-1039</link>
      <guid>https://dev.to/grabbit/how-to-screenshot-a-list-of-urls-bulk-capture-for-research-and-audits-1039</guid>
      <description>&lt;p&gt;You have a spreadsheet, a sitemap, or a list of competitor pages, and you need a screenshot of each one. Doing it by hand means opening twenty tabs and pressing the capture key twenty times. This guide covers every method that actually works, from a free browser extension for a one-off batch to a scripted loop you can run on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;To screenshot a list of URLs, &lt;strong&gt;loop over the list and capture each URL once&lt;/strong&gt;. There is no magic "capture them all in one shot" primitive. Every tool, extension, or API does the same thing under the hood: it visits each page in turn and renders it. The real choice is &lt;em&gt;how&lt;/em&gt; you drive that loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-off, a handful of URLs:&lt;/strong&gt; a browser extension that reads a pasted list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeatable, or part of a pipeline:&lt;/strong&gt; a short script that calls a screenshot API once per URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You already write capture code:&lt;/strong&gt; keep your headless browser, just point the loop at a managed renderer if local Chromium is flaky in CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 1: a browser extension (for a quick one-off)
&lt;/h2&gt;

&lt;p&gt;If you just need to grab a dozen pages once and never again, a Chrome extension like &lt;strong&gt;Multiple Screenshots&lt;/strong&gt; is the fastest path. You paste your list, it opens each page, captures it, and saves the files locally. No code.&lt;/p&gt;

&lt;p&gt;The limits show up the moment the job repeats. Extensions run in your browser, on your machine, with your session. You cannot put that in CI, schedule it overnight, or hand it to a teammate as a reproducible step. For a true one-time audit, fine. For anything you will do again, it is a dead end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 2: an open-source script (DIY headless)
&lt;/h2&gt;

&lt;p&gt;The classic developer answer is a script that drives a headless browser over the list. Tools like &lt;code&gt;webscreenshot&lt;/code&gt; or a few lines of Puppeteer read a file of URLs and capture each one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;urls.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`out/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fullPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, and it is free. What it does not solve is everything &lt;em&gt;around&lt;/em&gt; the capture: keeping Chromium installed and patched, handling pages that block headless browsers, dealing with cookie banners, and surviving in a CI runner or serverless function where a real browser binary is awkward to ship. Those problems scale with the size of your list. See &lt;a href="https://www.grabbit.live/blog/screenshot-from-url" rel="noopener noreferrer"&gt;screenshot a website from a URL&lt;/a&gt; for a fuller comparison of the DIY path versus an API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 3: a screenshot API in a loop (repeatable)
&lt;/h2&gt;

&lt;p&gt;When the job has to run more than once, a screenshot API turns each capture into a single HTTP request, so the loop is the whole program. You send a URL, you get back a hosted image. No browser to install, nothing to keep patched.&lt;/p&gt;

&lt;p&gt;Here is the full pattern in Node. Read the list, capture each URL, and collect the hosted image URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://stripe.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://vercel.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.grabbit.live/v1/grabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer sk_live_...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;full_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each response includes a hosted &lt;code&gt;image_url&lt;/code&gt;, so you can drop the results straight into a CSV, a database, or a dashboard. Because every request is independent, you can keep the loop sequential for simplicity, or add limited concurrency if you want the batch to finish faster. This is exactly the kind of repeatable, scriptable job &lt;a href="https://www.grabbit.live/automated-screenshots" rel="noopener noreferrer"&gt;Grabbit's automated screenshots&lt;/a&gt; are built for, and you can wire it up with a free test key before adding a card.&lt;/p&gt;

&lt;p&gt;A note on honesty: Grabbit does not expose a single "send me an array of URLs" endpoint. One request captures one URL. The loop above &lt;em&gt;is&lt;/em&gt; the bulk pattern, and it keeps each capture independent so one bad URL never sinks the whole batch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing per-URL options
&lt;/h2&gt;

&lt;p&gt;Each call in the loop is configured on its own, so a mixed list is no problem. The parameters that matter for a bulk job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;full_page: true&lt;/code&gt;&lt;/strong&gt; captures the entire scrollable page, not just the viewport. Essential for audits where you want the whole page on record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;width&lt;/code&gt;&lt;/strong&gt; sets the viewport (320 to 1920). Use a consistent width across the list so the screenshots line up for comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;format&lt;/code&gt;&lt;/strong&gt; can be &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, or &lt;code&gt;webp&lt;/code&gt;. WebP keeps a large batch small on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;delay_ms&lt;/code&gt;&lt;/strong&gt; waits up to 10 seconds before the capture, which helps on pages that fade content in after load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a large list where you do not want to hold the connection open per request, the same endpoint accepts &lt;code&gt;Prefer: respond-async&lt;/code&gt; (or &lt;code&gt;?async=true&lt;/code&gt;) and returns immediately with a job you poll later. That keeps a thousand-URL run from blocking your script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk screenshots for audits and monitoring
&lt;/h2&gt;

&lt;p&gt;The reason most people want to screenshot a list of URLs is not the screenshots themselves, it is the comparison. Capture your competitors' landing pages today, capture them again next month, and the diff tells a story. Save each &lt;code&gt;image_url&lt;/code&gt; next to its source URL and a timestamp, and you have the raw material for &lt;a href="https://www.grabbit.live/blog/website-change-monitoring" rel="noopener noreferrer"&gt;website change monitoring&lt;/a&gt; without building a crawler from scratch.&lt;/p&gt;

&lt;p&gt;The same loop powers a few common jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SEO audits:&lt;/strong&gt; snapshot every page in a sitemap to spot rendering or layout regressions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitor tracking:&lt;/strong&gt; re-run the same list on a schedule and compare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QA evidence:&lt;/strong&gt; capture key pages on every deploy and attach them to the release.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Screenshotting a list of URLs always comes down to a loop. A browser extension covers a one-time batch; a DIY script works until local Chromium becomes the maintenance burden; a screenshot API turns the whole job into a few lines that run anywhere, including CI and serverless. Point the loop at your list, collect the hosted image URLs, and you have a repeatable bulk-capture pipeline. To automate it end to end, see &lt;a href="https://www.grabbit.live/automated-screenshots" rel="noopener noreferrer"&gt;Grabbit's automated screenshots&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/screenshot-list-of-urls" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Claude Code Pricing 2026: Pro vs Max vs Team (Updated June)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Tue, 07 Jul 2026 11:12:38 +0000</pubDate>
      <link>https://dev.to/braingrid/claude-code-pricing-2026-pro-vs-max-vs-team-updated-june-3hmj</link>
      <guid>https://dev.to/braingrid/claude-code-pricing-2026-pro-vs-max-vs-team-updated-june-3hmj</guid>
      <description>&lt;p&gt;AI coding assistants like Claude Code, ChatGPT, Cursor, and Windsurf are changing how founders ship software. These tools accelerate development for experienced engineers and enable non-technical SaaS founders to build working prototypes without hiring a dev team.&lt;/p&gt;

&lt;p&gt;But how much does it all cost? How much will a team spend on tools like Claude Code, and how can they prevent bill shock at the end of the month from overages? Read on to understand how to use Claude Code efficiently and avoid burning tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much Does Claude Code Cost? Quick Overview
&lt;/h2&gt;

&lt;p&gt;Visiting &lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;Claude.ai&lt;/a&gt; lets you chat with Claude's latest models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1mftpbm0f4hwpz98ehlw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1mftpbm0f4hwpz98ehlw.jpg" alt="chatting with claude" width="799" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But free Claude access does &lt;strong&gt;NOT&lt;/strong&gt; include Claude Code. Access to Claude Code requires a monthly subscription.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Claude Code Access&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$20/mo&lt;/td&gt;
&lt;td&gt;✓ Full access&lt;/td&gt;
&lt;td&gt;Daily coding, solo founders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max 5x&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$100/mo&lt;/td&gt;
&lt;td&gt;✓ Extended limits&lt;/td&gt;
&lt;td&gt;Heavy users, complex projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max 20x&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$200/mo&lt;/td&gt;
&lt;td&gt;✓ Maximum capacity&lt;/td&gt;
&lt;td&gt;Power users, all-day coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team (standard seat)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$25/user/mo ($20 annually)&lt;/td&gt;
&lt;td&gt;✗ Not included&lt;/td&gt;
&lt;td&gt;Collaboration, research, admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team (premium seat)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$125/user/mo ($100 annually)&lt;/td&gt;
&lt;td&gt;✓ Plus Cowork&lt;/td&gt;
&lt;td&gt;Organizations, dev teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API-only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pay-per-token&lt;/td&gt;
&lt;td&gt;Via terminal&lt;/td&gt;
&lt;td&gt;Custom integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Team plan trap:&lt;/strong&gt; standard Team seats do NOT include Claude Code. If your team signed up to build, the developers need premium seats. Full decision guide in our &lt;a href="https://www.braingrid.ai/blog/claude-pro-vs-team-vs-max" rel="noopener noreferrer"&gt;Claude Pro vs Team vs Max breakdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that Anthropic offers yearly discounts on annual subscriptions - Claude Code Pro is $17/mo (15% savings) when purchased annually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each Claude Code subscription includes session limits—a maximum number of prompts you can send within a rolling 5-hour window. Claude Code Pro subscribers can expect a maximum &lt;a href="https://support.claude.com/en/articles/11145838-using-claude-code-with-your-pro-or-max-plan" rel="noopener noreferrer"&gt;45 messages per 5-hour window&lt;/a&gt;. However, coding prompts consume more tokens than simple chat queries, so you'll realistically get &lt;a href="https://support.claude.com/en/articles/11145838-using-claude-code-with-your-pro-or-max-plan" rel="noopener noreferrer"&gt;10-40 prompts&lt;/a&gt; per window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9h8olyobn3j8ivoqkyy3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9h8olyobn3j8ivoqkyy3.jpg" alt="Screenshot of Hitting the limit" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the worst feeling: You're in flow state, shipping a critical feature, and Claude Code says "You've reached your usage limit. Try again in 3 hours." Your momentum is gone. Your code progress halts for hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; If you're racing to a demo, investor meeting, or launch deadline, losing 2-3 hours could mean missing your target.&lt;/p&gt;

&lt;p&gt;You can turn on extra usage in your settings to keep Claude Code going. Set a monthly maximum you're comfortable with. Anthropic's &lt;a href="https://code.claude.com/docs/en/costs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; reports the average cost is $6/day, with 90% of developers under $12/day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foxwx594ioikcaxkjgyv1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foxwx594ioikcaxkjgyv1.jpg" alt="Turning on extra usage with Claude Code" width="799" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any extra usage is paid at the API token pricing:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;API Token Pricing&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input (per 1M tokens)&lt;/th&gt;
&lt;th&gt;Output (per 1M tokens)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Haiku 4.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sonnet 4.6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$3&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Opus 4.8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;$25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fable 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Claude Pro vs Max: Which Plan Should You Choose?
&lt;/h2&gt;

&lt;p&gt;The Max plans cost more but include significantly more token capacity. Claude Max 5x costs $100/month and includes 5x the tokens of Pro. Claude Max 20x gives you 20x the tokens for $200/month.&lt;/p&gt;

&lt;p&gt;For regular daily coding, troubleshooting, and planning, Claude Pro is sufficient. You may find a few days of heavy coding leads to extra usage charges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to upgrade to Max 5x:&lt;/strong&gt; If you're hitting Pro's limits daily and spending $4+ on extra usage, Max 5x becomes cost-effective. Here's the math:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$4/day × 22 working days = $88 in overages&lt;/li&gt;
&lt;li&gt;Add your $20 Pro subscription = $108/month total&lt;/li&gt;
&lt;li&gt;Max 5x costs $100/month flat with 5x the capacity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your overages consistently exceed $80/month, Max 5x saves money and removes the usage anxiety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code Teams and Enterprise: When Do You Need Them?
&lt;/h2&gt;

&lt;p&gt;Claude Code on the Team plan requires a premium seat at $125/month per developer ($100/month billed annually). The plan has a 5-seat minimum across seat types, with standard seats at $25/month ($20 annually) for teammates who collaborate but do not code. Five premium seats run $625/month; a mixed team of two developers on premium seats plus three standard seats is about $325/month.&lt;/p&gt;

&lt;p&gt;Premium seats carry roughly 6.25x Pro's usage per session (5x a standard seat), slightly above Max 5x. But the real benefit is governance: your data is excluded from model training, &lt;a href="https://www.braingrid.ai/blog/cursor-mcp" rel="noopener noreferrer"&gt;Model Context Protocol (MCP) servers&lt;/a&gt; are configured at the team level, so when a developer leaves, you automatically revoke their access to internal databases and tooling, and Team provides centralized billing and usage auditing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The decision framework:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two founders building a product? Use two individual accounts; the 5-seat minimum makes Team expensive for you.&lt;/li&gt;
&lt;li&gt;Growing past 5 developers? Security, permissioning, and data controls become critical. Team makes sense, with premium seats for the builders only.&lt;/li&gt;
&lt;li&gt;Need SOC2 compliance or audit trails? Team is the path forward, and Enterprise ($20/seat plus API-rate usage) adds HIPAA options and SCIM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full plan-choice walkthrough, including when individual Max plans beat Team seats, see &lt;a href="https://www.braingrid.ai/blog/claude-pro-vs-team-vs-max" rel="noopener noreferrer"&gt;Claude Pro vs Team vs Max&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Reduce Claude Code Costs: Practical Efficiency Strategies
&lt;/h2&gt;

&lt;p&gt;Now that you know the pricing tiers, let's talk about the real cost lever: how efficiently you use tokens.&lt;/p&gt;

&lt;p&gt;Every question, code review, and bug fix burns tokens. But you can cut &lt;em&gt;how many&lt;/em&gt; tokens each query consumes. These strategies can reduce your bill by 50-80%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;/clear&lt;/code&gt; between tasks
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/clear&lt;/code&gt; command tells Claude Code to forget the current conversation context. Say you're squashing bugs: after fixing a frontend CSS issue, you jump to a backend database query. There's no need for Claude to keep the CSS conversation in memory. Clear it out for better results and fewer tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to &lt;code&gt;/clear&lt;/code&gt;:&lt;/strong&gt; Switching between unrelated files, moving from frontend to backend, or jumping from feature work to bug fixes. If Claude's suggestions start referencing the wrong context, you waited too long.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;/compact&lt;/code&gt; to summarize the conversation
&lt;/h3&gt;

&lt;p&gt;When building a feature over several iterations, every code snippet gets shared every time. The context window grows larger, meaning more tokens per question.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/compact&lt;/code&gt; command tells Claude Code to summarize the current chat and start fresh with the "Cliff's Notes" version. The conversation continues with all pertinent details but uses a fraction of the tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Use &lt;code&gt;/compact&lt;/code&gt; after 10-15 messages or when &lt;code&gt;/cost&lt;/code&gt; shows over 5M tokens in your session. You'll know you waited too long when Claude starts repeating itself or missing context from earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check your usage with &lt;code&gt;/cost&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/cost&lt;/code&gt; slash command shows token usage for your current session. Use this to understand your usage patterns and identify which tasks burn the most tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure &lt;code&gt;.claudeignore&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;.gitignore&lt;/code&gt; tells Git which files to skip, &lt;code&gt;.claudeignore&lt;/code&gt; tells Claude what files to ignore when analyzing your codebase. Are you accidentally sending your entire &lt;code&gt;node_modules&lt;/code&gt; folder to Claude with every prompt? That's 50-90% wasted tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Create &lt;code&gt;.claudeignore&lt;/code&gt; in your project root with these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.gitignore
node_modules/
.next/
build/
dist/
&lt;span class="k"&gt;*&lt;/span&gt;.log
.env&lt;span class="k"&gt;*&lt;/span&gt;
.git/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;/cost&lt;/code&gt; before and after to verify the reduction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model Selection Strategy
&lt;/h3&gt;

&lt;p&gt;Different Claude models have different costs. Match the model to the task:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task Type&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Autocomplete, simple edits&lt;/td&gt;
&lt;td&gt;Haiku 4.5&lt;/td&gt;
&lt;td&gt;Fastest, cheapest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature implementation&lt;/td&gt;
&lt;td&gt;Sonnet 4.6&lt;/td&gt;
&lt;td&gt;Best balance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex debugging&lt;/td&gt;
&lt;td&gt;Sonnet 4.6&lt;/td&gt;
&lt;td&gt;Usually sufficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture decisions&lt;/td&gt;
&lt;td&gt;Opus 4.8&lt;/td&gt;
&lt;td&gt;Worth the premium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Critical production code&lt;/td&gt;
&lt;td&gt;Opus 4.8&lt;/td&gt;
&lt;td&gt;When it matters most&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The rule:&lt;/strong&gt; Sonnet handles 80% of your work. Reserve Opus for complex reasoning tasks where Sonnet gets stuck. This alone can reduce token costs by 30-40%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to break the rule:&lt;/strong&gt; If Haiku gives buggy code twice, jump straight to Sonnet. Don't burn three cheap iterations when one good Sonnet response costs less.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Biggest Token Saver: Spec-Driven Development
&lt;/h3&gt;

&lt;p&gt;All the tactics above (clearing, compacting, model switching) save 10-40% on tokens. But they miss the real problem: rebuilding the same feature over and over because the requirements weren't clear. &lt;a href="https://www.braingrid.ai/blog/spec-driven-development" rel="noopener noreferrer"&gt;Spec-driven development&lt;/a&gt; solves this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The expensive way:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prompt: "Build user authentication"&lt;/li&gt;
&lt;li&gt;Claude builds it (5,000 tokens)&lt;/li&gt;
&lt;li&gt;You: "Actually, I need email verification"&lt;/li&gt;
&lt;li&gt;Claude rebuilds (6,000 tokens)&lt;/li&gt;
&lt;li&gt;You: "And password reset"&lt;/li&gt;
&lt;li&gt;Claude rebuilds again (7,000 tokens)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 18,000 tokens, 3 rebuilds, one frustrated afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spec-first workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a clear requirement before coding&lt;/li&gt;
&lt;li&gt;Break it into specific tasks&lt;/li&gt;
&lt;li&gt;Implement each task with Claude understanding the full context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the right prompt built from a clear specification, you can build the feature correctly the first time. That's 60-80% token savings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; formalizes this workflow, turning vague ideas into structured specs that Claude can implement in one pass. Give BrainGrid a prompt of what you'd like to build, and BrainGrid asks implementation questions and feature questions, just like a product manager fleshing out the details of what should be built. A requirements document is created, which you can use to generate a list of tasks.&lt;/p&gt;

&lt;p&gt;Connecting &lt;a href="https://docs.braingrid.ai/claude-code" rel="noopener noreferrer"&gt;BrainGrid to Claude Code&lt;/a&gt; allows Claude Code to read the requirements and tasks, then implement them directly from the command line. For the full setup, see our &lt;a href="https://www.braingrid.ai/blog/claude-code-mcp" rel="noopener noreferrer"&gt;Claude Code MCP servers guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Spec-driven development ensures the prompts provided to Claude Code are complete and will build the expected feature the first time. Eliminate the iteration rabbit hole to save a huge percentage of token usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Claude Code Worth It? The Solo Founder's Decision Framework
&lt;/h2&gt;

&lt;p&gt;If you value your time at $50/hour, Claude Code needs to save you 24 minutes per month to break even. That's one debugging session.&lt;/p&gt;

&lt;p&gt;For a solo founder shipping products, a $20 Claude Code Pro account is worth it if it saves one hour of debugging. Any additional time savings from feature coding, prototyping, or writing tests is bonus ROI.&lt;/p&gt;

&lt;p&gt;If Claude Code helps you shave 10-30% from each dev cycle (or increase output per cycle by 10-30%), that's $20 very well spent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maximize your $20/month:&lt;/strong&gt; Pair Claude Code with &lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; to turn ideas into shippable specs in minutes, or integrate with other agentic tools like Cursor to further accelerate how quickly you and your team ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much does Claude Code cost?
&lt;/h3&gt;

&lt;p&gt;Claude Code Pro is $20/month ($17/month when paid yearly). Claude Code Max ranges from $100-200/month for power users. On the Team plan, Claude Code requires a premium seat at $125/month per developer ($100/month billed annually).&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Claude Code free?
&lt;/h3&gt;

&lt;p&gt;No. A free Claude account allows chat on the web, but no access to Claude Code. You need a paid subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between Claude Pro and Max?
&lt;/h3&gt;

&lt;p&gt;Claude Pro is $20/month. Claude Max starts at $100/month and includes 5x the token capacity. &lt;strong&gt;Upgrade trigger:&lt;/strong&gt; If you're spending $80+/month on overages, Max 5x is more cost-effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is Claude Code using so many tokens?
&lt;/h3&gt;

&lt;p&gt;When iterating on a problem, Claude Code processes the full conversation context with each response. Long conversations with lots of code snippets burn tokens fast. Use &lt;code&gt;/clear&lt;/code&gt; between tasks and &lt;code&gt;/compact&lt;/code&gt; during long sessions to reduce usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I reduce Claude Code costs?
&lt;/h3&gt;

&lt;p&gt;The biggest lever is reducing iterations. Great prompts from tools like BrainGrid's spec-driven development ensure you build features correctly the first time. Fewer iterations = fewer tokens = lower costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Claude Code worth it?
&lt;/h3&gt;

&lt;p&gt;If it saves you one hour of debugging per month, you've broken even (assuming your time is worth $20+/hour). Most founders report saving 5-10 hours monthly. &lt;strong&gt;Try Pro for 30 days&lt;/strong&gt; and track time saved vs. rate limit waits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code vs Cursor: which is cheaper?
&lt;/h3&gt;

&lt;p&gt;Both have a $20/month basic plan. Cursor includes an IDE and routes queries to different LLMs automatically. Claude Code gives you direct access to Claude's full model lineup. For most founders, pick based on workflow preference, not price. See our &lt;a href="https://www.braingrid.ai/blog/cursor-pricing" rel="noopener noreferrer"&gt;detailed Cursor pricing breakdown&lt;/a&gt; for more.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are Claude Code rate limits?
&lt;/h3&gt;

&lt;p&gt;Claude Code has a 5-hour rolling window for token usage. Hit the limit and you'll see "Try again in X hours." &lt;strong&gt;Avoid surprises:&lt;/strong&gt; Enable extra usage with a monthly cap (Settings &amp;gt; Billing). If you're regularly hitting limits, consider Max 5x.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/claude-code-pricing" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Engineering project management without the overhead</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 06 Jul 2026 13:15:04 +0000</pubDate>
      <link>https://dev.to/radial/engineering-project-management-without-the-overhead-5g21</link>
      <guid>https://dev.to/radial/engineering-project-management-without-the-overhead-5g21</guid>
      <description>&lt;p&gt;Engineering project management is the practice of planning, tracking, and delivering software work: turning a pile of "we should do this" into scoped issues, moving them through a workflow, and knowing at any moment what is in flight, what is blocked, and what shipped. That is the whole job. Everything else a tool piles on top of it, the roadmaps, the portfolio views, the burndown charts, the AI copilots, is optional, and most of it is overhead.&lt;/p&gt;

&lt;p&gt;This post is about the lean version. Not project management theory, and not the enterprise suite. The specific question a software team actually has: what is the smallest tool that lets us plan and ship the work without the process becoming the work?&lt;/p&gt;

&lt;h2&gt;
  
  
  What engineering project management actually requires
&lt;/h2&gt;

&lt;p&gt;Strip it down and a software team needs four things from a tracker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A place to write down the work.&lt;/strong&gt; Issues with a title, an owner, a priority, and enough description to act on. That is the atom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A workflow to move it through.&lt;/strong&gt; Backlog to in progress to done, with a triage step for the stuff that arrives unsorted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A way to batch it.&lt;/strong&gt; A sprint or cycle so "this two weeks" is a real, bounded thing and not an open-ended list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A shared view of state.&lt;/strong&gt; So anyone, a teammate or an agent, can answer "what is the status of X" without a standup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is engineering project management for a software team. Notice what is not on the list: Gantt charts, resource-leveling, earned-value tracking, a roadmap timeline, velocity dashboards. Those belong to a different discipline, the capital-P Project Management that runs bridges and multi-year hardware programs. Bolting that machinery onto a software team is how a tracker turns into the thing everyone quietly avoids.&lt;/p&gt;

&lt;p&gt;Radial ships exactly the four things above and stops there on purpose. Issues, a triage queue, board and list layouts, Cycles (time-boxed sprints), estimates, and projects to group related work. It does not ship burndown charts, velocity graphs, a roadmap timeline, or portfolio management, and that is a deliberate line, not a gap we are rushing to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The overhead is the process, not the tool
&lt;/h2&gt;

&lt;p&gt;The reason engineering project management gets a bad name is not the tracking. It is the ceremony that accretes around it. A workflow with eleven states. Required custom fields on every ticket. A weekly ritual of updating a status you already communicated in the pull request. The tool grew a feature, someone turned it on, and now it is a tax on every issue forever.&lt;/p&gt;

&lt;p&gt;The fix is not a better dashboard. It is less surface area. The best engineering project management setup is the one your team forgets it is using, because filing an issue costs a keystroke and the status is obvious from the work itself. Speed matters here for a concrete reason: if the tracker is slow, people route around it, and a tracker people route around stops being the source of truth. Then you are back to reconstructing state from Slack and memory.&lt;/p&gt;

&lt;p&gt;So the lean version has two properties. It is fast enough that using it is never the friction. And it does one thing, issue tracking, well enough that you do not need a second tool to compensate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the work from where you already are
&lt;/h2&gt;

&lt;p&gt;For an engineering team, the highest-leverage property of a tracker is that it can be driven from the terminal, not just clicked through a web app. When the tracker has a real CLI, project management stops being a separate context you switch into and becomes something you script into the work you are already doing.&lt;/p&gt;

&lt;p&gt;Every Radial command takes &lt;code&gt;--json&lt;/code&gt;, so the same operations your team does by hand also run from a script, a CI job, or an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# File an issue on the engineering team, high priority, from the terminal or CI&lt;/span&gt;
radial create &lt;span class="s2"&gt;"Investigate 500s on checkout"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ENG &lt;span class="nt"&gt;-p&lt;/span&gt; high &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# See exactly what is on your plate right now, as data you can pipe&lt;/span&gt;
radial list &lt;span class="nt"&gt;--assignee&lt;/span&gt; me &lt;span class="nt"&gt;--status&lt;/span&gt; &lt;span class="s2"&gt;"in progress"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;--json&lt;/code&gt; is on everything, the tracker becomes programmable. A failing CI build can file its own issue. A pre-commit hook can check what is assigned to you. And your coding agent can do the same operations over MCP, because to the API there is no difference between you typing &lt;code&gt;radial create&lt;/code&gt; and Claude Code calling the same endpoint. Every credential is a client of the API, not a billed seat, so your agents ride free. That is the shape of engineering project management that fits how software teams actually work in 2026: the human and the agent writing to one shared record, from the terminal, without a per-agent bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill is part of the overhead too
&lt;/h2&gt;

&lt;p&gt;There is a second kind of overhead that does not show up in the workflow: the invoice. Most trackers are per seat per month, and most now meter AI work on top of that, a credit allowance per seat, overage billing, a separate charge per agent. The number you sign up for is not the number you pay, and the gap grows exactly as your team leans into agents.&lt;/p&gt;

&lt;p&gt;Radial is one number: &lt;strong&gt;$50 per user, per year, flat, billed annually, locked at the rate you join.&lt;/strong&gt; No per-agent seats, no AI credits, no usage meter, because there is no AI in the product to meter. The part that makes it a commitment rather than a slogan is the Plain Software Pledge, written down: the day Radial ships a copilot, meters your usage, or charges you for AI you did not ask for, your subscription is free. This is not anti-AI. Your agent doing real work is great. The tracker is just not where that intelligence should live or get billed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this approach is honestly the wrong fit
&lt;/h2&gt;

&lt;p&gt;A fair pitch names the edges. If your "engineering project management" actually means running a program office, resource-planning across fifty teams, reporting up a burndown to leadership, or managing a hardware project with a fixed-sequence Gantt schedule, a lean issue tracker is not that tool, and Radial is not pretending to be. Jira and the heavier Work OS platforms exist for that breadth, and if it is load-bearing for you, stay.&lt;/p&gt;

&lt;p&gt;The lean version is for engineering-led teams that ship software and want the tracking to disappear into the work. If that is you, the overhead you have been tolerating was never the point of project management. It was just the tool getting in the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is engineering project management?
&lt;/h3&gt;

&lt;p&gt;It is the practice of planning, tracking, and delivering engineering work. For a software team specifically, that means scoping work into issues, moving them through a workflow, batching them into sprints or cycles, and keeping a shared view of what is in progress, blocked, and shipped. Broader definitions from other engineering disciplines add scheduling, budgeting, and resource allocation for large technical programs, but a software team's version is narrower: it is issue tracking done well, not a program-management suite.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the best project management tool for software development?
&lt;/h3&gt;

&lt;p&gt;There is no single answer, because it depends on what you need. Teams that want maximum breadth and configurability tend toward Jira or Azure DevOps. Teams that want speed and a keyboard-first workflow lean toward focused trackers. Teams glued to their repository stay in GitHub Issues until a flat list stops scaling. The honest filter for an engineering team is: pick the fastest tool that does issue tracking well and resist the ones that make you adopt a whole planning methodology to file a ticket. If a scriptable CLI and agents that ride free matter to you, that narrows the field further.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the phases of a software development project?
&lt;/h3&gt;

&lt;p&gt;Most frameworks name six: initiation, planning, design, development, testing, and deployment or maintenance. The lifecycle is useful as a mental model, but from a tracker's point of view all six reduce to the same primitives: issues that carry the work, a workflow that reflects where each one is, and cycles that bound how much you take on at once. You do not need a tool with a "phase" feature. You need issues that move.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do engineers or project managers manage the work?
&lt;/h3&gt;

&lt;p&gt;On lean software teams, increasingly the engineers do, directly, which is exactly why the tracker being fast and scriptable matters. When filing and updating issues costs a keystroke or a terminal command instead of a meeting, engineers keep the record current as a byproduct of doing the work. A dedicated PM role still adds value coordinating across teams and stakeholders, but the day-to-day tracking should not require one. The overhead-heavy setups are the ones that do.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do coding agents fit into engineering project management?
&lt;/h3&gt;

&lt;p&gt;The same way a teammate does: they read and write the tracker. In Radial, an agent connects over the MCP server or the CLI and can file issues, triage the queue, update status, and close work, using the identical API surface a human uses. Because every credential is an API client rather than a billed seat, running agents against your tracker does not add a line to the invoice. That is the practical version of "the tracker is the shared system of record," a record both the human and the agent write to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Engineering project management is a small job wearing a big coat. The job is: write down the work, move it through a workflow, batch it into cycles, and keep one shared view of state. The coat is everything the tools pile on top, the dashboards, the ceremony, the AI meter on the invoice.&lt;/p&gt;

&lt;p&gt;Radial is the lean version: a fast keyboard-first tracker, scriptable from the terminal, one flat locked $50 per user per year, and agents that ride free. See the one number on &lt;a href="https://radial.build/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;, or read how the same CLI turns the tracker into something you script in &lt;a href="https://radial.build/blog/linear-cli" rel="noopener noreferrer"&gt;the Linear CLI Linear never shipped&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/engineering-project-management" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Screenshots for AI Agents: Giving Your LLM Eyes on the Web</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 06 Jul 2026 11:39:03 +0000</pubDate>
      <link>https://dev.to/grabbit/screenshots-for-ai-agents-giving-your-llm-eyes-on-the-web-2nee</link>
      <guid>https://dev.to/grabbit/screenshots-for-ai-agents-giving-your-llm-eyes-on-the-web-2nee</guid>
      <description>&lt;p&gt;Most LLMs process text. But a growing category of agent tasks is fundamentally visual: checking whether a deploy broke a page layout, verifying that an OG card looks right before a post goes live, monitoring a competitor's pricing page for changes. Text cannot do these jobs. A screenshot can.&lt;/p&gt;

&lt;p&gt;The pattern is straightforward: capture a screenshot via API, get back a hosted image URL, pass that URL to a vision model. No headless browser to manage, no Puppeteer session to keep alive, no parsing fragile DOM trees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why text-only agents hit a wall
&lt;/h2&gt;

&lt;p&gt;HTML is a poor proxy for what a user sees. Dynamic frameworks render client-side; the raw HTML is a template with no content. CSS controls what is visible and what is not. Interstitials, cookie banners, and lazy-loaded images change the actual visual state. A text-based agent scraping HTML sees none of this.&lt;/p&gt;

&lt;p&gt;Vision models bypass the problem entirely. They look at the page the way a user does. The prerequisite: they need an image to analyze. That is what the screenshot API provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The capture-and-analyze pattern
&lt;/h2&gt;

&lt;p&gt;The core loop for a vision-capable agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;seeUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.grabbit.live/v1/grabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GRABBIT_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;grab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;grab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a hosted URL, ready to pass to any vision model&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function returns a URL your agent passes directly to whatever vision model it uses as an image input. The image is already hosted; the vision model fetches it. No base64 encoding, no file handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capture params that matter for agents
&lt;/h2&gt;

&lt;p&gt;Three params make the most difference in agentic contexts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;selector&lt;/code&gt;&lt;/strong&gt; waits for a specific element to appear in the DOM, then captures just that element. Useful when you care about one component, not the whole page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://yoursite.com/dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#revenue-chart"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API returns an image of the &lt;code&gt;#revenue-chart&lt;/code&gt; element. Pass that to a vision model to read a number, detect a visual anomaly, or compare it to a previous capture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;full_page&lt;/code&gt;&lt;/strong&gt; captures the entire document height, not just the initial viewport. Set this when your agent needs to see content that loads below the fold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;delay_ms&lt;/code&gt;&lt;/strong&gt; adds a fixed wait after page load. If the page fetches data from an API and renders it into the DOM, a &lt;code&gt;delay_ms&lt;/code&gt; of 500 to 1000 gives the script time to finish before the capture fires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic use cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Automated visual QA.&lt;/strong&gt; After each deploy, an agent screenshots key pages and passes them to a vision model with a prompt describing what to look for. The model spots a broken nav, a missing hero image, or an overlapping button before a human does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OG card verification.&lt;/strong&gt; Before a post goes live, an agent screenshots the &lt;code&gt;/og?title=...&lt;/code&gt; route at 1200 by 630, passes the image to a vision model, and confirms the title, author, and brand logo are all present and not clipped. See &lt;a href="https://www.grabbit.live/blog/og-image-generator" rel="noopener noreferrer"&gt;How to Generate Dynamic OG Images from Any URL&lt;/a&gt; for the template pattern this builds on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring competitor pages.&lt;/strong&gt; Capture a competitor's pricing page on a schedule, then diff the image against the previous capture using a vision model. An alert fires when the layout or text changes significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual data extraction.&lt;/strong&gt; Some dashboards render data into charts with no accessible API. A screenshot of the chart, analyzed by a vision model, extracts the numbers without reverse-engineering a private API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering the tool in an agent
&lt;/h2&gt;

&lt;p&gt;The pattern is the same across frameworks: define a function tool that accepts a URL and returns the &lt;code&gt;image_url&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenshotTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screenshot_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Capture a screenshot of a web page and return a hosted image URL for visual analysis.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The page to screenshot.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Optional CSS selector. Waits for this element and captures it.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;full_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Capture the full document height.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Milliseconds to wait after page load before capturing.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the agent framework calls this tool and the function returns &lt;code&gt;image_url&lt;/code&gt;, include it in the next LLM message as an image block. The model sees the page and can reason about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  From agent tool to MCP server
&lt;/h2&gt;

&lt;p&gt;If you use an MCP-compatible host, you can expose screenshot capability as an MCP tool. The agent calls &lt;code&gt;screenshot_url&lt;/code&gt;, gets back an image URL, and uses it in the next turn. Grabbit is designed for exactly this: one API key, one endpoint, no browser runtime to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go further
&lt;/h2&gt;

&lt;p&gt;For running this pattern on a full page set on a CI schedule, &lt;a href="https://www.grabbit.live/blog/visual-regression-testing" rel="noopener noreferrer"&gt;Visual Regression Testing: A Practical Guide&lt;/a&gt; covers the screenshot-and-compare approach in depth.&lt;/p&gt;

&lt;p&gt;For capturing any URL on demand from your own code, see the &lt;a href="https://www.grabbit.live/screenshot-api" rel="noopener noreferrer"&gt;Grabbit screenshot API&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/screenshots-for-ai-agents" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>startup</category>
      <category>saas</category>
    </item>
    <item>
      <title>Cursor Pricing 2026: Free vs Pro vs Ultra — Which Plan?</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 06 Jul 2026 11:12:17 +0000</pubDate>
      <link>https://dev.to/braingrid/cursor-pricing-2026-free-vs-pro-vs-ultra-which-plan-hdd</link>
      <guid>https://dev.to/braingrid/cursor-pricing-2026-free-vs-pro-vs-ultra-which-plan-hdd</guid>
      <description>&lt;p&gt;Cursor is a popular Agentic AI coding tool, helping developers improve productivity, and even helping SaaS founders with no coding background build fully functional systems. Unlike other agentic tools like Copilot or Claude Code which are primarily editor extensions, Cursor is a standalone IDE (Integrated Development Environment) that replaces the need for using VS Code (it is actually a fork of VS Code). It melds a native coding experience with a AI-first conversational coding solution.&lt;/p&gt;

&lt;p&gt;Agentic AI tools like Cursor speed development and improve developer performance. The LLM agents inside Cursor do not just blindly add code to your repository.  When prompted with a code change, the AI Agents read your existing code, write code that fits into your existing codebase - matching styles and reading across files and modules to ensure that the generated code is compatible with the entire repository.&lt;/p&gt;

&lt;p&gt;But, how much does such an AI tool cost?  While the Cursor AI &lt;a href="https://cursor.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; provides the basic details, this post aims to break down the available plans so that your team can determine the price point most appropriate for your usage needs and help you understand when you need to upgrade to a more advanced plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Cursor Brings to the Table
&lt;/h2&gt;

&lt;p&gt;To understand the pricing of Cursor, it is important to understand the features that Cursor brings to your development team.  As previously mentioned, Cursor is a full-fledged IDE that developers can use for coding. The AI Agents inside Cursor have many features that improve coding workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full repository awareness&lt;/strong&gt;: Code analysis isn't limited to a single function or file—Cursor understands your full repository and how changes affect other files, modules, and dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP integrations&lt;/strong&gt;: Cursor has native &lt;a href="https://www.braingrid.ai/blog/cursor-mcp" rel="noopener noreferrer"&gt;Model Context Protocol support&lt;/a&gt; to pull context from &lt;em&gt;outside&lt;/em&gt; your repo—APIs, documentation, databases, or internal tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple file changes&lt;/strong&gt;: Plans and applies multi-file diffs when creating code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural language&lt;/strong&gt;: Build code using plain English prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File safety&lt;/strong&gt;: Git-based file safety makes it easy to review, accept, reject, or roll back changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter the pricing tier - these features are included with your Cursor subscription.  The major differences between the Cursor pricing plans comes from &lt;strong&gt;how much&lt;/strong&gt; Agentic access is provided, but we will also dig into the subtle differences.&lt;/p&gt;

&lt;p&gt;We'll kick off our review of plans designed for a single developer: what Cursor calls "Individual plans."  These plans are best for a founder or developer coding alone on a project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hobby Tier: Starting with Cursor
&lt;/h3&gt;

&lt;p&gt;Never used Cursor? The free "Hobby" tier lets you test the tool and see if Cursor's workflows fit your existing processes. Being free, the Hobby plan provides very limited access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tab completions: Cursor's coding autocomplete feature lets you build faster - by tabbing the suggested autocompletes from Cursor.&lt;/li&gt;
&lt;li&gt;Agent requests: Chat with Cursor's agents to code or debug your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a great way to experiment with Cursor before buying a paid plan. As you might expect, the number of Agent requests is small—you may complete one feature (or only part of one) before running out of credits and being prompted to upgrade.&lt;/p&gt;

&lt;p&gt;Get stuck mid-feature on the Hobby tier? You can activate a 7-day free Pro trial to help you complete your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pro ($20/month): The Sweet Spot for Most Founders
&lt;/h3&gt;

&lt;p&gt;The Pro plan is Cursor's most popular tier: at $20/month for one user, it's an easy entry point that won't break the bank. You get everything in Hobby, plus higher limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlimited tab completions: Tab completions are virtually unlimited on the Pro plan.&lt;/li&gt;
&lt;li&gt;Extended limits on Agent Requests:  While not unlimited, the number of Agentic Requests is much higher.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Pro plan also introduces the ability to run &lt;em&gt;Background Agents&lt;/em&gt;. You can ask Cursor to work on a task in the background, while you continue to use the Agent in your current coding. The Background agent works asynchronously to the developer's work - coming back only when it has an answer, or has a clarifying question.&lt;/p&gt;

&lt;p&gt;The number of Agent Requests and Background Agents provided in Cursor's Pro plan are provided quantitatively. When an Agentic call is made, Cursor breaks the task up for different agents, and utilizes common LLMs (OpenAI, Gemini and Claude are commonly used) to 'solve' the problem.  The Pro plan includes a fixed monthly allowance of AI compute, roughly equivalent to what Cursor internally budgets for a $20/month user. Token usage is abstracted away from the developer—there is no visible token counting, only usage-based limits.&lt;/p&gt;

&lt;p&gt;Should a developer utilize all of the Agent requests allocated in a month, there are two options: the team can either block overage charges, and advanced agent features pause or degrade until the next billing cycle, or the team can cap the amount of overage charges that can be accrued in the month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2to138xqyvnzrtlk0jd1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2to138xqyvnzrtlk0jd1.png" alt="Overage charge settings in Cursor" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pro+ ($60/month) and Ultra ($200/month): For Heavy Users
&lt;/h3&gt;

&lt;p&gt;Once developers begin using Cursor, they may find that they become heavy users, and they reach the limits of the Pro plan.  The Pro+ and Ultra plans are built for these developers.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;heavy&lt;/em&gt; users of Cursor, the Pro+ plan (at $60 per month) provides everything in the Pro plan, but provides 3x more usage with the top models - allowing for more background tasks, and Agentic problem solving.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;extremely heavy&lt;/em&gt; users, Cursor's Ultra plan provides 20x the usage of the Pro plan - for $200 a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Team and Enterprise plans
&lt;/h2&gt;

&lt;p&gt;For side projects or solo development, Individual plans make sense—you know your usage and can pick accordingly. But as your team grows, managing individual plans becomes a bookkeeping nightmare and introduces security concerns.&lt;/p&gt;

&lt;p&gt;This is where Cursor's team and enterprise plans fit in.&lt;/p&gt;

&lt;p&gt;Cursor's team plan costs $40 per month per user, fitting between Pro and Pro+ individual accounts.&lt;/p&gt;

&lt;p&gt;Beyond centralized billing and easy team member management, the major advantage is improved data isolation and privacy controls. Prompts and code analyzed on a Teams account are &lt;strong&gt;not&lt;/strong&gt; used to train models.&lt;/p&gt;

&lt;p&gt;The biggest privacy win? MCP access is controlled at the team level. Proprietary data from MCPs stays managed by your organization—it can't leak to individual accounts when developers come and go.&lt;/p&gt;

&lt;p&gt;For teams with stricter privacy requirements, the Enterprise plan adds region-specific data handling (data stays in your country/region), audit logging, and deeper admin visibility. Enterprise pricing isn't published—you'll need to work with Cursor's sales team for a custom contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addons: Bugbot
&lt;/h2&gt;

&lt;p&gt;Cursor offers Bugbot - a product that scans every PR on Github for potential bugs. It acts like a senior dev that reviews the code, looking for bugs in code being submitted for review.  The Pro and Teams versions of Bugbot cost $40 per user per month, in addition to the Cursor subscription. While this may seem expensive at first, Bugbot delivers the highest ROI for teams with junior or mid-level developers by catching common bugs before senior engineers need to review the code. It acts as a first-pass reviewer, reducing review noise and protecting senior developer time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further extending Cursor: BrainGrid's spec driven development
&lt;/h2&gt;

&lt;p&gt;Cursor's Agentic coding AI raises the bar and improves productivity of development teams. But all developers who use AI know about falling into 'rabbit holes' where the AI is just not interpreting the prompts well, and hours are spent getting the AI back on track and to have it stop making similar mistakes over and over.&lt;/p&gt;

&lt;p&gt;Many times, features are built &lt;em&gt;at the prompt&lt;/em&gt; with little forethought or planning. As the feature evolves, so too do the prompts and the underlying code.&lt;/p&gt;

&lt;p&gt;In traditional development processes, Product Managers work with the stakeholders to plan what the feature looks like, what it should (and should not) do, and build a roadmap of how the feature will evolve.  BrainGrid acts as a product-management layer for AI coding workflows - helping developers take ideas for features, and building requirements around the feature.  BrainGrid asks clarifying questions to help the product owner better understand what is being built, and what is out of scope.  This &lt;a href="https://www.braingrid.ai/blog/spec-driven-development" rel="noopener noreferrer"&gt;spec driven development&lt;/a&gt; leads to the creation of a requirements document that describes the scope of the feature and what is going to be built.  The team can analyze and make changes.&lt;/p&gt;

&lt;p&gt;Once the requirements document is approved, BrainGrid creates tasks with prompts that can be fed directly into Cursor.  The prompts are highly detailed, which means that there are fewer back and forth iterations with the Agentic AI to actually build the feature. See an example of feature building with BrainGrid and Cursor in our &lt;a href="https://www.braingrid.ai/blog/windsurf-vs-cursor" rel="noopener noreferrer"&gt;Windsurf vs. Cursor comparison&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Connecting the BrainGrid MCP into Cursor ensures that features are better understood and fleshed out - instead of just building at the prompt.  This results in faster development, fewer rabbit holes, and higher productivity.&lt;/p&gt;

&lt;p&gt;Starting at $10/month/user (to $75/month/user for enterprises), BrainGrid's product driven specifications gives Cursor additional superpowers, further accelerating your team's development productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Cursor's paid plans start at $20/month and scale to $200/month for the Ultra tier. For development teams, the Team and Enterprise plans simplify billing while adding privacy and security controls. And for teams going &lt;em&gt;all-in&lt;/em&gt; with agentic coding, spec-driven development tools like BrainGrid help you build better prompts—leading to focused code and fewer AI rabbit holes.&lt;/p&gt;

&lt;p&gt;To learn more, visit &lt;a href="https://cursor.ai" rel="noopener noreferrer"&gt;Cursor.ai&lt;/a&gt; or explore &lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;spec-driven development at BrainGrid.ai&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How much does Cursor Team cost?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Cursor Team starts at $40/month per user.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How costly is Cursor?&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hobby&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Testing Cursor before committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$20/mo&lt;/td&gt;
&lt;td&gt;Solo founders shipping MVPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pro+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$60/mo&lt;/td&gt;
&lt;td&gt;Heavy daily users hitting Pro limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ultra&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$200/mo&lt;/td&gt;
&lt;td&gt;Full-time AI-assisted development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$40/user/mo&lt;/td&gt;
&lt;td&gt;Startups with 2+ developers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enterprise&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Regulated industries, large orgs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Is Cursor AI free or paid?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There's a free Hobby tier, but paid plans start at $20/month.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Is Cursor better than Copilot?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;They're different tools. Copilot generates code snippets; Cursor understands your full repository and builds code that fits your existing codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Do you need a subscription for Cursor?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Yes, though the Hobby tier is free for testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Is Cursor's free plan good?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It's like a taste of ice cream—enough to evaluate, but most real-world development requires a paid plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How much do different models cost in Cursor?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By default, Cursor uses Auto mode, that uses internal tooling to pick the bets agent for a given task. This can be overridden, and developers can choose specific Agents (like Opus 4.5, Sonnet 4.5, ChatGPT 5.2, and Gemini 3) for all queries.  Cursor has published cost per million tokens for many of the most popular and powerful models in their &lt;a href="https://cursor.com/docs/models#model-pricing" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/cursor-pricing" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Take a Website Screenshot in Node.js (4 Ways)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Sun, 05 Jul 2026 11:37:11 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-take-a-website-screenshot-in-nodejs-4-ways-8h3</link>
      <guid>https://dev.to/grabbit/how-to-take-a-website-screenshot-in-nodejs-4-ways-8h3</guid>
      <description>&lt;p&gt;Node.js has no shortage of ways to screenshot a website. The honest list is four: Puppeteer, Playwright, a thin wrapper like &lt;code&gt;capture-website&lt;/code&gt;, or a hosted API. The first three all run Chromium on your server; the fourth does not. This guide shows each with working code and the trade-off that decides which one you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Puppeteer (the default)
&lt;/h2&gt;

&lt;p&gt;Puppeteer is the most common choice and drives headless Chrome directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fullPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The detail that matters is &lt;code&gt;waitUntil: 'networkidle0'&lt;/code&gt;, which waits for the network to settle before capturing so you do not screenshot a half-loaded page. &lt;code&gt;fullPage: true&lt;/code&gt; captures the whole scrolling page. For elements, quality, and the lazy-load gotchas, see &lt;a href="https://www.grabbit.live/blog/puppeteer-screenshot" rel="noopener noreferrer"&gt;full-page screenshots in Puppeteer&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Playwright (cross-browser)
&lt;/h2&gt;

&lt;p&gt;Playwright has nearly identical code and adds Firefox and WebKit with one API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fullPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical wins over Puppeteer are auto-waiting (less timing code) and being able to capture the same page in three engines, including the engine behind Safari. See &lt;a href="https://www.grabbit.live/blog/playwright-screenshot" rel="noopener noreferrer"&gt;screenshots in Playwright&lt;/a&gt; for the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. capture-website (the short wrapper)
&lt;/h2&gt;

&lt;p&gt;If you just want one screenshot and do not want to manage a browser lifecycle, &lt;code&gt;capture-website&lt;/code&gt; wraps Puppeteer into one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;captureWebsite&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;capture-website&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;captureWebsite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fullPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It still installs and runs Chromium under the hood, so the operational cost is the same as Puppeteer. It just hides the boilerplate. Avoid older packages like &lt;code&gt;node-webshot&lt;/code&gt; that wrap the abandoned PhantomJS; do not build new code on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost the first three share
&lt;/h2&gt;

&lt;p&gt;All three options above mean you run a browser on your server. None of the hard parts are the screenshot call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provisioning Chromium.&lt;/strong&gt; It needs a long list of system libraries. Slim containers and serverless functions hit missing-dependency errors before the first capture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory and zombie processes.&lt;/strong&gt; A browser not closed on every error path leaks memory and orphans processes under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patching and scaling.&lt;/strong&gt; Chromium ships security updates constantly, and one browser handles one job at a time, so volume means a pool and a queue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If screenshots are a production feature rather than a script, this is infrastructure you now own.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A hosted API (no browser to run)
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://www.grabbit.live/screenshot-api" rel="noopener noreferrer"&gt;screenshot API&lt;/a&gt; runs the browser fleet for you. From Node.js it is one &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.grabbit.live/v1/grabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer sk_live_...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;full_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;image_url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is a hosted image URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grb_01jx..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.grabbit.live/grabs/grb_01jx....webp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bytes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"execution_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1180&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library options map onto request parameters: viewport is &lt;code&gt;width&lt;/code&gt; (320 to 1920) and &lt;code&gt;height&lt;/code&gt; (240 to 1080), full page is &lt;code&gt;full_page&lt;/code&gt;, the element pattern is a &lt;code&gt;selector&lt;/code&gt; field, and the manual wait becomes &lt;code&gt;delay_ms&lt;/code&gt; (0 to 10000). &lt;code&gt;format&lt;/code&gt; is &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, or &lt;code&gt;webp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which to use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Already running Puppeteer or Playwright&lt;/strong&gt; for tests or scraping: add the screenshot call, you are done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A quick one-off script:&lt;/strong&gt; &lt;code&gt;capture-website&lt;/code&gt; is the least code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screenshots are a production feature:&lt;/strong&gt; an API, so you do not operate Chromium in your Node service at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the framework deep dives, see &lt;a href="https://www.grabbit.live/blog/puppeteer-screenshot" rel="noopener noreferrer"&gt;screenshots in Puppeteer&lt;/a&gt; and &lt;a href="https://www.grabbit.live/blog/playwright-screenshot" rel="noopener noreferrer"&gt;screenshots in Playwright&lt;/a&gt;. If you are weighing hosted options, the &lt;a href="https://www.grabbit.live/blog/best-screenshot-api" rel="noopener noreferrer"&gt;honest comparison of screenshot APIs&lt;/a&gt; covers the trade-offs without the marketing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/nodejs-screenshot-website" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
    </item>
    <item>
      <title>Ralph Wiggum Plugin for Claude Code: Autonomous Coding Guide</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Sun, 05 Jul 2026 11:12:51 +0000</pubDate>
      <link>https://dev.to/braingrid/ralph-wiggum-plugin-for-claude-code-autonomous-coding-guide-2j6h</link>
      <guid>https://dev.to/braingrid/ralph-wiggum-plugin-for-claude-code-autonomous-coding-guide-2j6h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah2rndhb56ed1tqlpzan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah2rndhb56ed1tqlpzan.png" alt="Ralph Wiggum Plugin for Claude Code - Autonomous loops that keep working until tasks are complete" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You know that feeling when you're three hours deep into fixing tests, and each fix breaks something else?&lt;/p&gt;

&lt;p&gt;What if you could just walk away? Describe what you want, go grab dinner, and come back to working code?&lt;/p&gt;

&lt;p&gt;That's Ralph Wiggum.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "Ralph Wiggum"?
&lt;/h2&gt;

&lt;p&gt;Remember Ralph Wiggum from The Simpsons? The kid who keeps trying despite failing spectacularly every single time?&lt;/p&gt;

&lt;p&gt;That's exactly what this plugin does. It fails, learns, tries again, and keeps going until it succeeds. No complaints. No giving up. Just persistent iteration until the job is done.&lt;/p&gt;

&lt;p&gt;The creator, Geoffrey Huntley, captured it perfectly: &lt;strong&gt;"Ralph is a Bash loop."&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; :&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;PROMPT.md | claude &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep running the same prompt until it works. That's Ralph.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Ralph Wiggum Plugin for Claude Code?
&lt;/h2&gt;

&lt;p&gt;The Ralph Wiggum plugin is Claude Code's official plugin that enables autonomous loops—letting AI work on its own for hours without you watching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normal Claude Code:&lt;/strong&gt; You ask → It does → You check → You ask again → Repeat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ralph Wiggum Plugin:&lt;/strong&gt; You tell Ralph what you want → Walk away → Come back to finished work. The autonomous loop handles everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Autonomous Loop in Claude Code?
&lt;/h2&gt;

&lt;p&gt;An autonomous loop is AI that keeps working without you. The Ralph Wiggum plugin creates these autonomous loops in Claude Code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You give Claude Code a task with clear "done" criteria&lt;/li&gt;
&lt;li&gt;Claude Code tries to complete it&lt;/li&gt;
&lt;li&gt;When Claude tries to stop, the Ralph Wiggum plugin checks if it's actually done&lt;/li&gt;
&lt;li&gt;If not done: Ralph restarts Claude Code with the same task&lt;/li&gt;
&lt;li&gt;Claude Code sees previous attempts and tries a different approach&lt;/li&gt;
&lt;li&gt;The autonomous loop repeats until done or hits your iteration limit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The key:&lt;/strong&gt; Each loop is smarter because it learns from previous failures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    Start([You: Give task with 'done' criteria]) --&amp;gt; Claude[Claude attempts task]
    Claude --&amp;gt; Ralph{Ralph checks:&amp;lt;br/&amp;gt;Is it done?}
    Ralph --&amp;gt;|Not done| Restart[Ralph restarts Claude&amp;lt;br/&amp;gt;with same task]
    Restart --&amp;gt; Learn[Claude sees previous attempts&amp;lt;br/&amp;gt;tries different approach]
    Learn --&amp;gt; Claude
    Ralph --&amp;gt;|Done| Success([✅ Task Complete])
    Ralph --&amp;gt;|Max iterations| Limit([⏱️ Hit iteration limit])

    style Start fill:#C2E476,stroke:#795BC2,stroke-width:2px,color:#0B211F
    style Success fill:#C2E476,stroke:#795BC2,stroke-width:2px,color:#0B211F
    style Limit fill:#795BC2,stroke:#C2E476,stroke-width:2px,color:#fff
    style Ralph fill:#795BC2,stroke:#C2E476,stroke-width:2px,color:#fff
    style Claude fill:#10312D,stroke:#795BC2,stroke-width:2px,color:#C2E476
    style Restart fill:#10312D,stroke:#C2E476,stroke-width:2px,color:#C2E476
    style Learn fill:#10312D,stroke:#C2E476,stroke-width:2px,color:#C2E476
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Do We Need This?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Normal Problem
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "Add user authentication"
Claude: *generates code*
You: "No, use JWT tokens"
Claude: *updates code*
You: "The password reset is missing"
Claude: *adds password reset*
You: "Tests are failing"
Claude: *fixes tests*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exhausting and slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ralph Solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "Add user authentication with JWT tokens, password reset,
and tests with &amp;gt;80% coverage. Done means all tests pass."
Ralph: *works for 2 hours*
Ralph: "Complete. All tests passing."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One detailed instruction. Walk away. Come back to finished work.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Ralph Wiggum Plugin (And When Not To)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Use Ralph For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mechanical tasks:&lt;/strong&gt; "Add JSDoc comments to every function"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tasks with tests:&lt;/strong&gt; Anything that can verify itself automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big refactors:&lt;/strong&gt; Framework upgrades, dependency updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overnight work:&lt;/strong&gt; Queue it Friday, review Monday&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Don't Use Ralph For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Judgment calls:&lt;/strong&gt; Architecture decisions, design choices, security code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague tasks:&lt;/strong&gt; "Make the UI better"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production fires:&lt;/strong&gt; When things are broken NOW&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick tasks:&lt;/strong&gt; If it takes 2 minutes manually, just do it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; If you can describe exactly what "done" looks like, the Ralph Wiggum plugin can probably do it using autonomous loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install and Use Ralph Wiggum Plugin: Quick Start Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ralph-wiggum@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Commands
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start an autonomous loop:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"&amp;lt;task&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start autonomous loop with auto-stop:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"&amp;lt;task&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 30 &lt;span class="nt"&gt;--completion-promise&lt;/span&gt; &lt;span class="s2"&gt;"COMPLETE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cancel Ralph Wiggum plugin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:cancel-ralph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Add Documentation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bad prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"Add comments to my functions"&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"Add JSDoc comments to every exported function in src/utils/.

Rules:
- Every exported function gets JSDoc
- Include @param, @returns, @throws
- Use clear descriptions

Done means: Every exported function has complete JSDoc.

Output &amp;lt;promise&amp;gt;DOCUMENTED&amp;lt;/promise&amp;gt; when complete."&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 20 &lt;span class="nt"&gt;--completion-promise&lt;/span&gt; &lt;span class="s2"&gt;"DOCUMENTED"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why the good prompt works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear scope (src/utils)&lt;/li&gt;
&lt;li&gt;Specific rules&lt;/li&gt;
&lt;li&gt;Clear "done" criteria&lt;/li&gt;
&lt;li&gt;Safety limit (20 iterations)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Secret: BrainGrid Makes Perfect Prompts
&lt;/h2&gt;

&lt;p&gt;Here's what everyone misses: &lt;strong&gt;The Ralph Wiggum plugin is only as good as your prompt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing good prompts is hard. You need to think through every edge case, acceptance criteria, and implementation detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is where BrainGrid changes everything.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Without BrainGrid
&lt;/h3&gt;

&lt;p&gt;Your vague idea: "Add user authentication"&lt;/p&gt;

&lt;p&gt;You write a prompt, but what does "authentication" mean? Email/password? OAuth? Password reset? Rate limiting? The Ralph Wiggum plugin burns iterations guessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  With BrainGrid MCP
&lt;/h3&gt;

&lt;p&gt;BrainGrid connects to Claude Code via MCP (Model Context Protocol), giving Claude direct access to structured requirements without copy-pasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Set up BrainGrid MCP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, connect BrainGrid to Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http braingrid https://mcp.braingrid.ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;braingrid&lt;/code&gt; listed. BrainGrid will prompt you to authorize via OAuth in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create a requirement in BrainGrid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Claude Code, ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Create a requirement in BrainGrid for user authentication system"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude (with BrainGrid MCP) creates a structured requirement with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear description&lt;/li&gt;
&lt;li&gt;Acceptance criteria&lt;/li&gt;
&lt;li&gt;Technical constraints&lt;/li&gt;
&lt;li&gt;Test requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Refine in BrainGrid dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="https://app.braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid AI Dashboard&lt;/a&gt; and work with the BrainGrid agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Help me refine this requirement and ask any clarifying questions"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent helps you catch missing details and edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Break down into tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the BrainGrid dashboard, click "Break down requirement into tasks." BrainGrid generates concrete, implementable tasks tied to REQ-456.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Build with Ralph + BrainGrid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now the magic happens. In Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"Build REQ-456 from BrainGrid"&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ralph automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches the full requirement from BrainGrid via MCP&lt;/li&gt;
&lt;li&gt;Reads all acceptance criteria&lt;/li&gt;
&lt;li&gt;Understands technical constraints&lt;/li&gt;
&lt;li&gt;Knows how to verify completion&lt;/li&gt;
&lt;li&gt;Builds with complete context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No copy-pasting. No missing details. Ralph just points at the BrainGrid requirement and builds it correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Difference
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Without BrainGrid:&lt;/strong&gt; Vague prompt → Ralph Wiggum plugin guesses → Wastes iterations → Wrong implementation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With BrainGrid MCP:&lt;/strong&gt; Detailed requirement → Ralph Wiggum plugin fetches full context → Builds it right the first time&lt;/p&gt;

&lt;h3&gt;
  
  
  The Complete Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[💡 Rough idea&amp;lt;br/&amp;gt;in Claude Code] --&amp;gt; B[📝 Create requirement&amp;lt;br/&amp;gt;in BrainGrid]
    B --&amp;gt; C[🔍 Refine requirement&amp;lt;br/&amp;gt;in BrainGrid dashboard]
    C --&amp;gt; D[📋 Break down into tasks&amp;lt;br/&amp;gt;in BrainGrid]
    D --&amp;gt; E[🤖 Build REQ-456&amp;lt;br/&amp;gt;with Ralph]
    E --&amp;gt; F[✅ Working feature&amp;lt;br/&amp;gt;with tests]

    style A fill:#C2E476,stroke:#795BC2,stroke-width:2px,color:#0B211F
    style B fill:#795BC2,stroke:#C2E476,stroke-width:2px,color:#fff
    style C fill:#795BC2,stroke:#C2E476,stroke-width:2px,color:#fff
    style D fill:#795BC2,stroke:#C2E476,stroke-width:2px,color:#fff
    style E fill:#10312D,stroke:#C2E476,stroke-width:2px,color:#C2E476
    style F fill:#C2E476,stroke:#795BC2,stroke-width:2px,color:#0B211F
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BrainGrid&lt;/strong&gt; = The thinking phase (what to build)&lt;br&gt;
&lt;strong&gt;Ralph&lt;/strong&gt; = The execution phase (building it)&lt;/p&gt;

&lt;p&gt;Most people skip the thinking phase and jump straight to execution. That's why their prompts fail and the Ralph Wiggum plugin spins endlessly in autonomous loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup guide:&lt;/strong&gt; &lt;a href="https://www.braingrid.ai/blog/claude-code-mcp" rel="noopener noreferrer"&gt;BrainGrid MCP Integration&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try BrainGrid free:&lt;/strong&gt; &lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;braingrid.ai&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ready-to-Use Templates
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Feature Implementation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"Build user profile edit feature.

What it needs:
- Form with name, email, bio fields
- Save button calls /api/users/me
- Show success/error messages
- All fields required except bio

Tests: Form renders, validation works, API called correctly

Done means: Feature works and tests pass.

Output &amp;lt;promise&amp;gt;COMPLETE&amp;lt;/promise&amp;gt; when done."&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Bug Fix
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/ralph-wiggum:ralph-loop &lt;span class="s2"&gt;"Fix: Users can submit empty contact form.

Steps:
1. Add validation for required fields (name, email, message)
2. Show error messages for empty fields
3. Prevent submission if validation fails
4. Add test to prevent regression

Done means: Empty form blocked and test proves it.

Output &amp;lt;promise&amp;gt;FIXED&amp;lt;/promise&amp;gt; when done."&lt;/span&gt; &lt;span class="nt"&gt;--max-iterations&lt;/span&gt; 15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Quick Tips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prompt Checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Clear scope (what files/features)&lt;/li&gt;
&lt;li&gt;✅ Specific requirements (what to do)&lt;/li&gt;
&lt;li&gt;✅ "Done" definition (how to verify)&lt;/li&gt;
&lt;li&gt;✅ Safety limit (--max-iterations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Iteration Guidelines:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple tasks: 10-20 iterations&lt;/li&gt;
&lt;li&gt;Medium features: 20-40 iterations&lt;/li&gt;
&lt;li&gt;Complex builds: 40-60 iterations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always set --max-iterations&lt;/strong&gt; (never unlimited)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost Estimates:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small task: $5-20&lt;/li&gt;
&lt;li&gt;Medium feature: $30-60&lt;/li&gt;
&lt;li&gt;Large build: $60-150+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Test manually with one iteration before running 50-iteration loops.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ralph Wiggum Plugin Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Loop never completes?&lt;/strong&gt;&lt;br&gt;
Your "done" criteria aren't clear enough. Be more specific.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Same error every loop?&lt;/strong&gt;&lt;br&gt;
Add to prompt: "If stuck after 10 tries, document the error and stop."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Costs too high?&lt;/strong&gt;&lt;br&gt;
Lower --max-iterations, use .claudeignore, break into smaller tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows issues?&lt;/strong&gt;&lt;br&gt;
Install jq: &lt;code&gt;choco install jq&lt;/code&gt; or use WSL.&lt;/p&gt;
&lt;h2&gt;
  
  
  Frequently Asked Questions About Ralph Wiggum Plugin
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What is the Ralph Wiggum plugin?
&lt;/h3&gt;

&lt;p&gt;The Ralph Wiggum plugin is an official Claude Code plugin that creates autonomous loops. It allows Claude Code to work continuously on a task, automatically restarting and trying different approaches until the task is complete or the iteration limit is reached.&lt;/p&gt;
&lt;h3&gt;
  
  
  How do autonomous loops work in Claude Code?
&lt;/h3&gt;

&lt;p&gt;Autonomous loops work by: (1) You provide a task with clear "done" criteria, (2) Claude Code attempts the task, (3) Ralph Wiggum checks if it's actually done, (4) If not done, Ralph restarts Claude with the same task, (5) Claude sees previous attempts and tries a different approach, (6) This repeats until done or the iteration limit is hit.&lt;/p&gt;
&lt;h3&gt;
  
  
  How do I install the Ralph Wiggum plugin?
&lt;/h3&gt;

&lt;p&gt;Install the Ralph Wiggum plugin in Claude Code using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ralph-wiggum@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What commands does the Ralph Wiggum plugin support?
&lt;/h3&gt;

&lt;p&gt;The Ralph Wiggum plugin supports three main commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a loop: &lt;code&gt;/ralph-wiggum:ralph-loop "&amp;lt;task&amp;gt;" --max-iterations 30&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start with auto-stop: &lt;code&gt;/ralph-wiggum:ralph-loop "&amp;lt;task&amp;gt;" --max-iterations 30 --completion-promise "COMPLETE"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cancel: &lt;code&gt;/ralph-wiggum:cancel-ralph&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When should I use Ralph Wiggum plugin?
&lt;/h3&gt;

&lt;p&gt;Use the Ralph Wiggum plugin for mechanical tasks, tasks with automated tests, big refactors, and overnight work. Don't use it for judgment calls, vague tasks, production emergencies, or quick 2-minute tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does Ralph Wiggum plugin cost?
&lt;/h3&gt;

&lt;p&gt;Cost estimates for Ralph Wiggum plugin usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small task: $5-20&lt;/li&gt;
&lt;li&gt;Medium feature: $30-60&lt;/li&gt;
&lt;li&gt;Large build: $60-150+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Can I use Ralph Wiggum with BrainGrid?
&lt;/h3&gt;

&lt;p&gt;Yes! BrainGrid MCP integration works perfectly with Ralph Wiggum plugin. BrainGrid helps create structured requirements that Ralph can fetch and build, resulting in better prompts and fewer wasted iterations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Ralph Wiggum plugin removes the iteration tax from mechanical work in Claude Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The shift:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; 3 hours of back-and-forth implementing a feature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; 10 minutes writing a clear prompt, then do other work while Ralph builds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start small. Document one folder. Fix linter errors in one directory. Build confidence in what prompts work.&lt;/p&gt;

&lt;p&gt;Then scale up. Queue overnight work. Run parallel loops. Let Ralph handle the boring parts while you focus on hard problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Ralph Wiggum plugin now:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin &lt;span class="nb"&gt;install &lt;/span&gt;ralph-wiggum@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Want perfect prompts every time?&lt;/strong&gt; BrainGrid helps you think through requirements so the Ralph Wiggum plugin builds it right the first time using autonomous loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;Try BrainGrid + Ralph free →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/ralph-wiggum-plugin" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Migrating from Linear to Radial in one command</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Sat, 04 Jul 2026 13:14:59 +0000</pubDate>
      <link>https://dev.to/radial/migrating-from-linear-to-radial-in-one-command-2fmc</link>
      <guid>https://dev.to/radial/migrating-from-linear-to-radial-in-one-command-2fmc</guid>
      <description>&lt;p&gt;Most people who search "migrate from linear" are not leaving because Linear is slow or badly built. It is neither. They are leaving because something about the shape of the tool stopped fitting: the seat math as the team grew, the AI features arriving whether they were asked for or not, or simply the pull toward a tracker their agents can drive without a per-vendor integration for each one. The decision is the hard part. The data move should not be.&lt;/p&gt;

&lt;p&gt;For Radial, the data move is a command. You export your Linear workspace, run &lt;code&gt;radial import --from linear&lt;/code&gt; against the file, and your issues, projects, labels, comments, and history come across in a single run. Dry-run it first so you see exactly what lands before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-minute version
&lt;/h2&gt;

&lt;p&gt;If you already have a Linear export on disk, the whole move is two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; radial.build
radial import &lt;span class="nt"&gt;--from&lt;/span&gt; linear export.json &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--dry-run&lt;/code&gt; flag does everything except write. It reports how many issues it would create, how many would fail, plus the relations, comments, and new labels it would build, and it surfaces warnings for anything it cannot map cleanly. When the dry run looks right, drop the flag and run it for real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial import &lt;span class="nt"&gt;--from&lt;/span&gt; linear export.json &lt;span class="nt"&gt;-t&lt;/span&gt; ENG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-t ENG&lt;/code&gt; puts the imported issues on your &lt;code&gt;ENG&lt;/code&gt; team. That is the entire migration for a single workspace. No connect-your-account handshake, no waiting on an import queue, no migration window to schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually comes across
&lt;/h2&gt;

&lt;p&gt;The import is a deep import, not a flat issue dump. In one run it brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issues&lt;/strong&gt;, with their titles, descriptions, and status mapped to Radial's workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects&lt;/strong&gt;, so the structure you had in Linear is not flattened into one bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels&lt;/strong&gt;, created as needed (the dry run tells you how many are new).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments&lt;/strong&gt;, preserved on the issues they belong to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History and relations&lt;/strong&gt;, so the links between issues survive the move.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the run finishes, you get a one-line summary of what landed: issues created, any that failed, plus relations, comments, and new labels created, followed by up to ten warnings for anything that needed a judgment call. Because every command takes &lt;code&gt;--json&lt;/code&gt;, you can also pipe that summary straight into a script if you are migrating several workspaces in a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial import &lt;span class="nt"&gt;--from&lt;/span&gt; linear export.json &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One honest caveat worth stating up front: the import is single-run. Re-running the same file imports it again rather than reconciling, so there is no cross-run idempotency yet. Dry-run first, run once, verify. If you are moving several teams, do them one file at a time and check each summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes, and what stays the same
&lt;/h2&gt;

&lt;p&gt;The fair thing to do in a migration guide is tell you what you are giving up, not just what you are gaining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What stays the same.&lt;/strong&gt; The core loop you are used to in Linear is the core loop in Radial: a fast keyboard-first board and list, issues with statuses and priorities and estimates, projects, and time-boxed Cycles where Linear has cycles. If speed is why you loved Linear, that part carries over; Radial is built the same way, for the same reason. Linear's famous internal target is that nothing in the interface should take more than a few hundred milliseconds to render, and that discipline is the right one. Radial holds the same bar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you give up.&lt;/strong&gt; Radial does not have Linear's full surface area. There is no roadmap or initiative timeline, no built-in insights or velocity dashboards, no Linear Asks-style intake from Slack, and the ecosystem of third-party integrations is smaller. If your team runs on Linear's roadmap and analytics views, that is a real gap to weigh before you export. Better to know now than to find out after.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changes for the better, if the bill was the reason.&lt;/strong&gt; This is the part the usual alternatives miss. The most common thing people wire into a modern tracker is an agent, and the most common thing that breaks is cost. A builder in r/Linear put it plainly while comparing agent-delegation setups: the biggest pain point is cost. Radial's answer is structural, not a discount. Every agent credential is a client of the API, not a billed seat, so your agents ride free. And the price you pay is one flat number: $50 per user, per year, billed annually, locked at the rate you join. No AI credit balance, no usage meter, no overage line, because there is no AI in the product to meter.&lt;/p&gt;

&lt;p&gt;The commitment behind that is the Plain Software Pledge, written down: the day Radial ships a copilot, meters your usage, or charges you for AI you didn't ask for, your subscription is free. This is not anti-AI. Your agent doing real work is great. The point is that the tracker is not where that intelligence should live or get billed; it is the fast system of record your agent writes to over a real CLI, REST API, and MCP server. One surface, any agent, no per-vendor integration to bolt on for each new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I export my data from Linear?
&lt;/h3&gt;

&lt;p&gt;Linear exports workspace and issue data from &lt;strong&gt;Settings &amp;gt; Administration &amp;gt; Import/Export&lt;/strong&gt;, and it also offers a CSV export for issue lists. Take that file and point Radial at it: &lt;code&gt;radial import --from linear export.json --dry-run&lt;/code&gt;. The dry run reads the file and reports exactly what it would create without writing anything, so you can confirm the mapping before the real run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Radial have a CLI to run the migration?
&lt;/h3&gt;

&lt;p&gt;Yes, and the CLI is how the migration runs. Radial is CLI-first by design: &lt;code&gt;radial import --from linear&lt;/code&gt; is a first-party command, not a community wrapper. Linear itself ships no official CLI (there are community CLIs and a GraphQL API), so if a terminal-native workflow is part of why you are switching, that is a real difference, not a marketing line. See &lt;a href="https://radial.build/blog/linear-cli" rel="noopener noreferrer"&gt;the Linear CLI Linear never shipped&lt;/a&gt; for the fuller story.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will I lose my comments and history?
&lt;/h3&gt;

&lt;p&gt;No. The import preserves comments on the issues they belong to and brings history and relations across, not just bare issue titles. The dry-run summary counts the comments, relations, and labels it would create, so you can verify the totals against Linear before you commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Radial just a cheaper Linear?
&lt;/h3&gt;

&lt;p&gt;No, and it is worth being precise: there are trackers cheaper than Radial, and Linear is a genuinely excellent tool. The difference is not the number, it is the shape. Radial is one flat locked price with agents riding free and a pledge that pays you if AI billing ever shows up, versus a per-seat plan with an AI layer that can grow. Pick by what made you leave, not by which line item is smaller. We lay out the full side-by-side in &lt;a href="https://radial.build/blog/radial-vs-linear" rel="noopener noreferrer"&gt;Radial vs Linear&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can my coding agent run the migration?
&lt;/h3&gt;

&lt;p&gt;Yes. The import is available over the REST API (&lt;code&gt;POST /v1/import&lt;/code&gt;) with a scoped API key, so an agent you authorize can run the move, or script it across several workspaces, without a human at the keyboard. Agents are clients of the API, not billed seats, so wiring one up costs nothing extra. Once you are on Radial, your agent files, triages, and closes issues over the MCP server at &lt;code&gt;mcp.radial.build&lt;/code&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Moving off Linear is usually gated by the data, not the decision. Radial makes the data the easy part: &lt;code&gt;radial import --from linear&lt;/code&gt; brings issues, projects, labels, comments, and history across in one run, and &lt;code&gt;--dry-run&lt;/code&gt; shows you the result before you commit. What you migrate into keeps the speed you liked, drops the surface you did not use, and holds one flat locked price with agents riding free.&lt;/p&gt;

&lt;p&gt;See the one number on &lt;a href="https://radial.build/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;, or read &lt;a href="https://radial.build/blog/radial-vs-linear" rel="noopener noreferrer"&gt;Radial vs Linear&lt;/a&gt; for the full comparison before you export.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/migrate-from-linear" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tools</category>
      <category>programming</category>
      <category>saas</category>
    </item>
    <item>
      <title>Playwright Visual Regression Testing: Catch UI Bugs in CI</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Sat, 04 Jul 2026 11:37:30 +0000</pubDate>
      <link>https://dev.to/grabbit/playwright-visual-regression-testing-catch-ui-bugs-in-ci-31eh</link>
      <guid>https://dev.to/grabbit/playwright-visual-regression-testing-catch-ui-bugs-in-ci-31eh</guid>
      <description>&lt;p&gt;Playwright has visual regression testing built in. One assertion, &lt;code&gt;toHaveScreenshot&lt;/code&gt;, captures the page, diffs it against a stored baseline, and fails the test when the UI changes unexpectedly. The setup is genuinely a few lines. The hard part is not the API; it is keeping baselines stable so CI catches real regressions instead of failing on font rendering. This guide covers both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core workflow: toHaveScreenshot
&lt;/h2&gt;

&lt;p&gt;A visual test is a normal Playwright test with one screenshot assertion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;homepage looks correct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveScreenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;homepage.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run, there is no baseline, so Playwright captures &lt;code&gt;homepage.png&lt;/code&gt; and saves it, and the test "fails" once to tell you a baseline was created. On every run after that, it captures a fresh screenshot, compares it pixel by pixel against the saved baseline, and passes only if they match within your threshold.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;toHaveScreenshot&lt;/code&gt; is the right assertion to reach for: it auto-waits for the page to stop changing and retries until the screenshot is stable, which removes a whole class of flaky-capture bugs. Prefer it over the older &lt;code&gt;toMatchSnapshot&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating baselines after an intentional change
&lt;/h2&gt;

&lt;p&gt;When you change the UI on purpose, the baseline is now wrong and the test should fail until you accept the new look. Regenerate baselines with a flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--update-snapshots&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rewrites the baseline images for any test whose capture changed. Commit those images so the new baseline travels with the code in Git, and reviewers can see the visual diff in the pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real problem: CI flake
&lt;/h2&gt;

&lt;p&gt;The most common frustration is tests that pass locally and fail in CI. This is almost never a real regression. It is the rendering environment: a different OS, different fonts, a different GPU, or a different browser build produces sub-pixel anti-aliasing differences that fail an exact-match comparison.&lt;/p&gt;

&lt;p&gt;Two fixes, used together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Generate baselines in the CI environment.&lt;/strong&gt; Do not commit baselines captured on your Mac and expect them to match a Linux CI runner. Generate them inside the same container that runs the tests, commonly the official Playwright Docker image, so the rendering matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Allow a small diff.&lt;/strong&gt; Set a tolerance so anti-aliasing noise does not fail the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveScreenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;homepage.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;maxDiffPixelRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// tolerate up to 1% differing pixels&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;maxDiffPixelRatio&lt;/code&gt; and &lt;code&gt;maxDiffPixels&lt;/code&gt; are the two knobs. Start strict and loosen only as far as you must to stop false failures, or you will mask real regressions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a screenshot API fits
&lt;/h2&gt;

&lt;p&gt;Playwright's &lt;code&gt;toHaveScreenshot&lt;/code&gt; is the right tool for asserting your own components and pages inside a Playwright suite. A &lt;a href="https://www.grabbit.live/automated-screenshots" rel="noopener noreferrer"&gt;screenshot API&lt;/a&gt; is not a replacement for it; it is a complementary capture layer for a few specific jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A stable, environment-independent baseline source.&lt;/strong&gt; Capturing the same URL through a hosted browser fleet gives you a consistent render that does not depend on each developer's machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capturing pages outside your test suite.&lt;/strong&gt; Monitoring a live production page or a third-party page you do not control, on a schedule, is a capture job, not a Playwright test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reference image of the real deployed page&lt;/strong&gt; to compare against, rather than a locally rendered one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a baseline capture as a single request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://example.com",
    "width": 1280,
    "full_page": true,
    "format": "png"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes a hosted image you can store as a reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grb_01jx..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.grabbit.live/grabs/grb_01jx....png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bytes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;96420&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"execution_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1240&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Width accepts 320 to 1920, height 240 to 1080, &lt;code&gt;full_page&lt;/code&gt; captures the whole page, and &lt;code&gt;delay_ms&lt;/code&gt; (0 to 10000) lets content settle before the shot. To be clear about scope: Grabbit captures the images. The diffing, the assertions, and the pass/fail live in Playwright or your VRT tool of choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;toHaveScreenshot&lt;/code&gt; for component and page assertions inside your Playwright suite.&lt;/li&gt;
&lt;li&gt;Generate baselines in the CI container and allow a small &lt;code&gt;maxDiffPixelRatio&lt;/code&gt; so you catch real regressions, not font noise.&lt;/li&gt;
&lt;li&gt;Reach for a screenshot API when you need a consistent baseline source or to capture pages that live outside the test suite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the bigger picture, see the &lt;a href="https://www.grabbit.live/blog/visual-regression-testing" rel="noopener noreferrer"&gt;practical guide to visual regression testing&lt;/a&gt; and the &lt;a href="https://www.grabbit.live/blog/visual-regression-testing-tools" rel="noopener noreferrer"&gt;roundup of visual regression testing tools&lt;/a&gt;. For Playwright screenshot mechanics beyond testing, see &lt;a href="https://www.grabbit.live/blog/playwright-screenshot" rel="noopener noreferrer"&gt;taking screenshots in Playwright&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/playwright-visual-regression-testing" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Windsurf MCP Servers: Setup Guide + Best Servers (2026)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Sat, 04 Jul 2026 11:12:57 +0000</pubDate>
      <link>https://dev.to/braingrid/windsurf-mcp-servers-setup-guide-best-servers-2026-1gg6</link>
      <guid>https://dev.to/braingrid/windsurf-mcp-servers-setup-guide-best-servers-2026-1gg6</guid>
      <description>&lt;p&gt;Every time Windsurf's AI writes database code, it guesses at your schema. Every API call gets placeholder responses you'll need to fix manually. That copy-paste cycle kills your momentum—and it doesn't have to be this way.&lt;/p&gt;

&lt;p&gt;Model Context Protocol (MCP) servers connect Windsurf to your actual databases, APIs, and external services. Instead of generating boilerplate that needs fixing, Windsurf's agents query your real systems and write code that works the first time.&lt;/p&gt;

&lt;p&gt;In this guide, you'll learn what MCP servers are, how to set them up in Windsurf, and which servers will help you ship faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MCP in Windsurf?
&lt;/h2&gt;

&lt;p&gt;MCP servers are bridges between Windsurf and external services. They give Windsurf's agents context beyond your codebase—databases, APIs, cloud infrastructure, and more.&lt;/p&gt;

&lt;p&gt;After connecting a database MCP server, Windsurf's agents can examine your actual tables and columns. The code it generates is based on real queries that retrieve data correctly, and does not rely on placeholder SQL that must be manually updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP Matters for Shipping Faster
&lt;/h2&gt;

&lt;p&gt;The same pattern applies across your entire stack. An API MCP server lets Windsurf call actual endpoints and write code that correctly parses real responses. A GitHub MCP server gives context about open issues and PRs.&lt;/p&gt;

&lt;p&gt;Without MCP, you're stuck fixing the same boilerplate over and over—copying real values into placeholder code. With MCP, the agents generate code that works the first time, letting you ship faster with fewer manual fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up MCP Servers in Windsurf
&lt;/h2&gt;

&lt;p&gt;Many MCP servers are available for Windsurf. Some install directly from the MCP Marketplace with a few clicks, while others require manual JSON configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing from the MCP Marketplace
&lt;/h3&gt;

&lt;p&gt;To access the MCP Marketplace, open Windsurf Settings (on Mac: &lt;strong&gt;Windsurf → Settings → Windsurf Settings&lt;/strong&gt; or press &lt;code&gt;⌘,&lt;/code&gt;). Search for "MCP" and click the link to open the Marketplace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo97yjzts0mg5f4nx0hu1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo97yjzts0mg5f4nx0hu1.jpg" alt="Windsurf MCP Marketplace in settings" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The marketplace offers many MCP servers ready to install.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqmnzvra4h1ta1bhwxs9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqmnzvra4h1ta1bhwxs9.jpg" alt="Windsurf MCP Marketplace showing available servers" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the PostgreSQL MCP server walks you through connection setup—host, port, credentials—and automatically populates the JSON config when complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj9qr7hp6nxcbqifbtfqg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj9qr7hp6nxcbqifbtfqg.jpg" alt="PostgreSQL MCP server setup in Windsurf" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing MCPs manually with JSON
&lt;/h3&gt;

&lt;p&gt;If your MCP server isn't in the Marketplace, you can add it manually by editing the &lt;code&gt;mcp_config.json&lt;/code&gt; file. Access this file from the Marketplace by clicking the gear icon.&lt;/p&gt;

&lt;p&gt;Here is an example &lt;code&gt;mcp_config.json&lt;/code&gt; file with two MCP servers set up, BrainGrid, and the PostgreSQL set up from the marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"braingrid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote@latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.braingrid.ai/mcp"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postgresql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--rm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"mcp/postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"postgresql://admin:admin@example.com:5432/my-secret-database"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This JSON file includes servers installed via the Marketplace as well as manually added ones. The Marketplace simplifies setup by properly formatting the JSON entry for you. The PostgreSQL MCP server creates a small local Docker container with Postgres utilities and your database connection details.&lt;/p&gt;

&lt;p&gt;For BrainGrid MCP installation, follow the &lt;a href="https://docs.braingrid.ai/mcp-server/installation#windsurf" rel="noopener noreferrer"&gt;official setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windsurf MCP Configuration Format
&lt;/h3&gt;

&lt;p&gt;Each MCP entry specifies a &lt;code&gt;command&lt;/code&gt; (like &lt;code&gt;npx&lt;/code&gt; or &lt;code&gt;docker&lt;/code&gt;) and &lt;code&gt;args&lt;/code&gt; (the arguments to pass). Windsurf runs these commands when it starts, establishing the MCP server connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top MCP Servers for Windsurf Developers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt;: There are MCP servers for most major databases and DB-as-a-service platforms (MySQL, postgres, MongoDB, Redis, Neon, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudOps&lt;/strong&gt;:  AWS and Heroku MCP servers give Windsurf insight into how the application is deployed and configured in the cloud.  Additionally access to logs and environment specific behavior can help Windsurf improve reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: Agents can read pull requests, inspect issues, and understand commit history. This repository content does not live in the codebase, but influences how the code should change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt;: Visibility into billing flows, customers, subscriptions, invoices, and webhooks. Agents can debug and reason about how the billing logic interacts with the codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BrainGrid&lt;/strong&gt;: Takes vague development ideas, turns them into explicit requirements, and breaks those requirements into structured tasks the agents can execute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swagger&lt;/strong&gt;: When agents are able to read the API specs, they can build to existing endpoints, and parse the results in based on actual responses from the API.  No more hallucinated or boilerplate API code to be fixed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that each MCP provides a number of tools to Windsurf, and that Windsurf places a cap of 100 tools that can be active.  This might mean enabling and disabling MCPs during different phases of coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  BrainGrid MCP: Spec-Driven Development in Windsurf
&lt;/h2&gt;

&lt;p&gt;BrainGrid is an AI product manager that helps developers plan, specify, and build new features. Often, developers jump right in and start prompting before they have a clear picture of what they're building. Running those initial ideas through BrainGrid turns them into structured requirements.&lt;/p&gt;

&lt;p&gt;For example, say you want to add a billing flow to your application. BrainGrid analyzes your existing code and asks clarifying questions: which payment providers, what subscription tiers, how to handle failures. These answers become a requirements document your team can approve—spec-driven development. BrainGrid then breaks the requirements into implementation tasks.&lt;/p&gt;

&lt;p&gt;The BrainGrid MCP lets Windsurf access these requirements and tasks directly. Instead of copying prompts into the chat, Windsurf reads the full context and builds the feature.&lt;/p&gt;

&lt;p&gt;The result: agents build exactly what you specified, with less cleanup afterward. Less context-switching, faster shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Windsurf MCP Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command not found&lt;/strong&gt;: If Docker isn't running locally, Docker-based MCPs will fail. Start Docker first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing dependencies&lt;/strong&gt;: Some MCPs require packages like &lt;code&gt;mcp-remote&lt;/code&gt;. Install them before configuring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changes not taking effect&lt;/strong&gt;: After installing new dependencies, restart Windsurf to pick up the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Shipping Faster Today
&lt;/h2&gt;

&lt;p&gt;MCP servers give Windsurf's agents the full picture: databases, APIs, issues, designs, and more. With this context, the code they produce is higher quality and far less likely to need the manual fixes you've come to expect from AI-generated code.&lt;/p&gt;

&lt;p&gt;When agents build closer to what you actually need, you ship faster. Less time fixing, more time launching.&lt;/p&gt;

&lt;p&gt;Ready to go further? &lt;a href="https://www.braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; adds spec-driven development to your workflow—turning vague ideas into structured requirements that generate precise prompts for Windsurf's agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h4&gt;
  
  
  What is MCP in Windsurf?
&lt;/h4&gt;

&lt;p&gt;MCP (Model Context Protocol) gives Windsurf's agents context beyond your codebase. This includes cloud services, databases, API specifications, or any third-party service that provides insight into how your application works.&lt;/p&gt;

&lt;h4&gt;
  
  
  How do I configure MCP servers in Windsurf?
&lt;/h4&gt;

&lt;p&gt;Use the MCP Marketplace for one-click installs, or manually edit the &lt;code&gt;mcp_config.json&lt;/code&gt; file with the server configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where is the Windsurf MCP config file located?
&lt;/h4&gt;

&lt;p&gt;The config file is at &lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;. The easiest way to access it is through the MCP Marketplace by clicking the settings gear icon.&lt;/p&gt;

&lt;h4&gt;
  
  
  What MCP servers work with Windsurf?
&lt;/h4&gt;

&lt;p&gt;Any MCP server using stdio (local) or http (remote) transport works with Windsurf.&lt;/p&gt;

&lt;h4&gt;
  
  
  How does Windsurf MCP compare to Cursor MCP?
&lt;/h4&gt;

&lt;p&gt;Both support MCP, but they use it differently. Cursor treats MCP as a transactional tool—query and respond. Windsurf integrates MCP into its agentic workflow, using the context for multi-step planning and chaining data across tasks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why is my Windsurf MCP server not working?
&lt;/h4&gt;

&lt;p&gt;Check the troubleshooting section above. The MCP Marketplace shows connection status—look there for specific error messages to diagnose the issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/windsurf-mcp" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Migrating off Jira with one command: radial import --from jira</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Fri, 03 Jul 2026 13:14:34 +0000</pubDate>
      <link>https://dev.to/radial/migrating-off-jira-with-one-command-radial-import-from-jira-4565</link>
      <guid>https://dev.to/radial/migrating-off-jira-with-one-command-radial-import-from-jira-4565</guid>
      <description>&lt;p&gt;Most Jira migration guides are about moving from one Jira to another: Server or Data Center to Cloud, run the Jira Cloud Migration Assistant, plan the maintenance window, pre-load users. That is a real project, and Atlassian's tooling handles it. This guide is about the other migration: the one where you are leaving Jira for a faster, focused tracker and the only thing standing between you and the switch is your data.&lt;/p&gt;

&lt;p&gt;For Radial, that part is a command. You export your Jira project, run &lt;code&gt;radial import --from jira&lt;/code&gt; against the file, and your issues, projects, labels, comments, and history come across in a single run. Dry-run it first so you see exactly what lands before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-minute version
&lt;/h2&gt;

&lt;p&gt;If you already have a Jira export on disk, the whole move is two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; radial.build
radial import &lt;span class="nt"&gt;--from&lt;/span&gt; jira export.json &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--dry-run&lt;/code&gt; flag does everything except write. It reports how many issues it would create, how many would fail, plus the relations, comments, and new labels it would build, and it surfaces warnings for anything it cannot map cleanly. When the dry run looks right, drop the flag and run it for real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial import &lt;span class="nt"&gt;--from&lt;/span&gt; jira export.json &lt;span class="nt"&gt;-t&lt;/span&gt; ENG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-t ENG&lt;/code&gt; puts the imported issues on your &lt;code&gt;ENG&lt;/code&gt; team. That is the entire migration for a single project. No assistant app, no maintenance window, no overage to budget for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually comes across
&lt;/h2&gt;

&lt;p&gt;The import is a deep import, not a flat issue dump. In one run it brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issues&lt;/strong&gt;, with their titles, descriptions, and status mapped to Radial's workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects&lt;/strong&gt;, so the structure you had in Jira is not flattened into one bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels&lt;/strong&gt;, created as needed (the dry run tells you how many are new).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments&lt;/strong&gt;, preserved on the issues they belong to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History and relations&lt;/strong&gt;, so the links between issues survive the move.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the run finishes, you get a one-line summary of what landed: issues created, any that failed, plus relations, comments, and new labels created, followed by up to ten warnings for anything that needed a judgment call. Because every command takes &lt;code&gt;--json&lt;/code&gt;, you can also pipe that summary straight into a script if you are migrating several projects in a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial import &lt;span class="nt"&gt;--from&lt;/span&gt; jira export.json &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One honest caveat worth stating up front: the import is single-run. Re-running the same file imports it again rather than reconciling, so there is no cross-run idempotency yet. Dry-run first, run once, verify. If you are moving many projects, do them one file at a time and check each summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why teams make this move, and why some shouldn't
&lt;/h2&gt;

&lt;p&gt;People do not leave Jira because it is bad. They leave because they adopted it for engineering work and never needed its enterprise breadth, and the weight became a tax they pay every day. If a board that takes seconds to load and a workflow you configured for a week is your pain, a fast tracker fixes it.&lt;/p&gt;

&lt;p&gt;But the reason that the usual alternatives miss is the bill. Jira is per seat per month, and its AI layer, Rovo, ships a monthly credit allowance per seat with overage billing, and Rovo Dev is a further charge per developer. The subscription is the base; the AI is a meter. Switch to another per-seat-plus-meter tracker and you have changed the logo, not the trajectory.&lt;/p&gt;

&lt;p&gt;Radial is a fast, keyboard-first issue tracker priced at one flat number: $50 per user, per year, billed annually, locked at the rate you join. There is no AI credit balance, no usage meter, and no overage line, because there is no AI in the product to meter. Your agents ride free: every agent credential is a client of the API, not a billed seat. The commitment behind that is the Plain Software Pledge, written down: the day Radial ships a copilot, meters your usage, or charges you for AI you didn't ask for, your subscription is free.&lt;/p&gt;

&lt;p&gt;This is not anti-AI. Your agent doing real work is great. The point is that the tracker is not where that intelligence should live or get billed; it is the fast system of record your agent writes to over a real CLI, REST API, and MCP server.&lt;/p&gt;

&lt;p&gt;A fair guide names the gaps, too. Radial does not have Jira's breadth: no portfolio or initiative layer, no burndown or velocity dashboards, no Gantt roadmap, no thousand-app marketplace, no cross-department workflow engine. If you genuinely run fifty teams with fifty workflows and a compliance audit suite, Jira is built for that and Radial is not. Better to know before you export than to discover it after.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What will replace Jira for my team?
&lt;/h3&gt;

&lt;p&gt;There is no universal replacement, because teams leave Jira for different reasons. If the pain is speed, a fast focused tracker is the fix; if it is data ownership, the self-hosted field (Plane, OpenProject) is real; if it is the bill that keeps moving, you want a tool with one flat price and no AI meter. Pick by the reason you are leaving, not by whichever tool tops a listicle. We sorted the full field in &lt;a href="https://radial.build/blog/jira-alternative" rel="noopener noreferrer"&gt;the best Jira alternative depends on what made you leave&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Jira being phased out?
&lt;/h3&gt;

&lt;p&gt;No. Jira remains dominant in enterprise, and Atlassian is migrating Data Center customers to cloud rather than retiring the product. What is shifting is narrower: teams that adopted Jira for engineering but never needed its enterprise breadth are increasingly moving to faster, focused trackers. That is a fit problem, not obsolescence.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I export my data from Jira?
&lt;/h3&gt;

&lt;p&gt;Jira can export project data to JSON (and individual issue lists to CSV) from its own admin and search-export tooling. Once you have that file, point Radial at it: &lt;code&gt;radial import --from jira export.json --dry-run&lt;/code&gt;. The dry run reads the file and reports exactly what it would create without writing anything, so you can confirm the mapping before the real run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will I lose my comments and history?
&lt;/h3&gt;

&lt;p&gt;No. The import preserves comments on the issues they belong to and brings history and relations across, not just bare issue titles. The dry-run summary counts the comments, relations, and labels it would create, so you can verify the totals against Jira before you commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can my coding agent run the migration?
&lt;/h3&gt;

&lt;p&gt;Yes. The import is available over the REST API (&lt;code&gt;POST /v1/import&lt;/code&gt;) with a scoped API key, so an agent you authorize can run the move, or script it across several projects, without a human at the keyboard. Agents are clients of the API, not billed seats, so wiring one up costs nothing extra. Once you are on Radial, your agent files, triages, and closes issues over the MCP server at &lt;code&gt;mcp.radial.build&lt;/code&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Moving off Jira is usually gated by the export, not the decision. Radial makes the export the easy part: &lt;code&gt;radial import --from jira&lt;/code&gt; brings issues, projects, labels, comments, and history across in one run, and &lt;code&gt;--dry-run&lt;/code&gt; shows you the result before you commit. What you migrate into is a fast tracker with one flat locked price, no AI meter, and a pledge that pays you if we ever add one.&lt;/p&gt;

&lt;p&gt;See the one number on &lt;a href="https://radial.build/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;, or read &lt;a href="https://radial.build/blog/why-is-jira-so-slow" rel="noopener noreferrer"&gt;why is Jira so slow&lt;/a&gt; for the engineering reason the weight is not free.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/jira-migration" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tools</category>
      <category>programming</category>
      <category>saas</category>
    </item>
    <item>
      <title>How to Screenshot an Entire Web Page on Any Device</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Fri, 03 Jul 2026 11:38:47 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-screenshot-an-entire-web-page-on-any-device-5c54</link>
      <guid>https://dev.to/grabbit/how-to-screenshot-an-entire-web-page-on-any-device-5c54</guid>
      <description>&lt;p&gt;Full-page screenshots are straightforward on desktop but awkward on mobile, and totally different again when you need them from code. This guide covers the real methods for each situation so you can pick the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quickest cross-device method
&lt;/h2&gt;

&lt;p&gt;If you are writing code, running a CI job, or building a tool that needs to capture pages from a server or agent, a screenshot API is the only method that works everywhere. The browser runs in the cloud, not on your device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://example.com",
    "width": 1280,
    "full_page": true,
    "format": "webp"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;full_page: true&lt;/code&gt; tells the renderer to scroll to the bottom of the page and stitch all the sections into one image. The response contains a hosted &lt;code&gt;image_url&lt;/code&gt; you can write directly to your database or pass to a front end. No browser install, no Puppeteer, no scrolling logic to maintain.&lt;/p&gt;

&lt;p&gt;For a broader look at programmatic capture, see &lt;a href="https://www.grabbit.live/blog/screenshot-from-url" rel="noopener noreferrer"&gt;how to screenshot a website from a URL&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  On desktop: Chrome DevTools
&lt;/h2&gt;

&lt;p&gt;Chrome includes a full-page screenshot command that requires no extensions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open DevTools: &lt;code&gt;F12&lt;/code&gt; on Windows or Linux, &lt;code&gt;Cmd + Opt + I&lt;/code&gt; on Mac.&lt;/li&gt;
&lt;li&gt;Open the command palette: &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; on Windows, &lt;code&gt;Cmd + Shift + P&lt;/code&gt; on Mac.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;full size screenshot&lt;/code&gt; and press Enter.&lt;/li&gt;
&lt;li&gt;Chrome saves a PNG of the full page to your Downloads folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The capture uses the same Chromium engine as most screenshot APIs, so the result looks accurate. The limitation is that it only works from a desktop browser and cannot be scripted. For more detail on this method, see &lt;a href="https://www.grabbit.live/blog/capture-full-page-screenshot-chrome" rel="noopener noreferrer"&gt;how to capture a full-page screenshot in Chrome&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  On desktop: Firefox
&lt;/h2&gt;

&lt;p&gt;Firefox exposes its screenshot tool without DevTools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click anywhere on the page and choose &lt;strong&gt;Take Screenshot&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save full page&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Firefox downloads a PNG of the entire scrollable page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No keyboard shortcuts to remember and no extensions needed. The output is always PNG.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Mac: Safari
&lt;/h2&gt;

&lt;p&gt;Safari has no native full-page screenshot shortcut. Your options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Chrome on the same machine (the DevTools method above).&lt;/li&gt;
&lt;li&gt;Install a browser extension such as GoFullPage or Nimbus Capture.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Cmd + Shift + 4&lt;/code&gt; to capture the visible area, then scroll and repeat (tedious and imprecise).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Safari is a hard requirement, a browser extension or the API is the path of least resistance.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Windows: built-in tools
&lt;/h2&gt;

&lt;p&gt;The Windows Snipping Tool (&lt;code&gt;Win + Shift + S&lt;/code&gt;) captures the visible viewport only. There is no built-in way to scroll and stitch from the OS level. Use the Chrome DevTools method or a browser extension to get the full page.&lt;/p&gt;

&lt;h2&gt;
  
  
  On iPhone (iOS)
&lt;/h2&gt;

&lt;p&gt;iOS 13 and later include a built-in full-page screenshot option inside Safari.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take a regular screenshot: Side button + Volume Up on Face ID iPhones, or Home + Power on older models.&lt;/li&gt;
&lt;li&gt;Tap the thumbnail that appears in the lower-left corner.&lt;/li&gt;
&lt;li&gt;Tap &lt;strong&gt;Full Page&lt;/strong&gt; at the top of the edit view.&lt;/li&gt;
&lt;li&gt;Tap &lt;strong&gt;Done&lt;/strong&gt; and save to Files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two things to know: the capture is saved as a &lt;strong&gt;PDF&lt;/strong&gt;, not a PNG or WebP, and this only works in Safari, not Chrome or Firefox on iOS. If you need an actual image file, use a browser extension or the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Android
&lt;/h2&gt;

&lt;p&gt;Android does not have a single universal answer here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Samsung Galaxy:&lt;/strong&gt; Take a screenshot normally (Power + Volume Down), then tap &lt;strong&gt;Scroll capture&lt;/strong&gt; in the overlay that appears at the bottom. Samsung stitches the scrolled sections automatically into one image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Pixel and most stock Android:&lt;/strong&gt; No built-in scrolling screenshot. The closest native option is Share &amp;gt; Print &amp;gt; Save as PDF in Chrome, which has the same PDF limitation as iOS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All Android browsers:&lt;/strong&gt; Several extensions for Chrome on Android (such as GoFullPage) add a one-tap full-page screenshot. This is the practical choice on non-Samsung devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the right method
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Best method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Desktop, one-off manual capture&lt;/td&gt;
&lt;td&gt;Chrome DevTools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop, Firefox user&lt;/td&gt;
&lt;td&gt;Firefox right-click &amp;gt; Save full page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iPhone, one-off&lt;/td&gt;
&lt;td&gt;iOS Safari full page (saves as PDF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Samsung Android&lt;/td&gt;
&lt;td&gt;Scroll capture in the screenshot overlay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other Android&lt;/td&gt;
&lt;td&gt;Browser extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server, script, or CI&lt;/td&gt;
&lt;td&gt;Screenshot API (&lt;code&gt;full_page: true&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile device, need an image file&lt;/td&gt;
&lt;td&gt;Screenshot API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI agent or automated workflow&lt;/td&gt;
&lt;td&gt;Screenshot API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern that works everywhere and in every format: the API. Native browser tools are fine for manual one-off captures but differ by platform, change with browser updates, and cannot be automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Chrome DevTools and Firefox cover desktop. iOS gives you full pages but only as PDFs in Safari. Android is split: Samsung has scrolling capture built in, everyone else needs an extension. For anything automated, cross-device, or outside a desktop browser, a screenshot API with &lt;code&gt;full_page: true&lt;/code&gt; is the consistent answer. Start capturing full pages programmatically with &lt;a href="https://www.grabbit.live/guides/screenshot-a-website" rel="noopener noreferrer"&gt;Grabbit's screenshot API&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/screenshot-entire-web-page" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
