<?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: Fareez Ahmed</title>
    <description>The latest articles on DEV Community by Fareez Ahmed (@fareez1234).</description>
    <link>https://dev.to/fareez1234</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%2F1347612%2F5c2ba67f-819b-41d3-9efa-1b7232dea335.png</url>
      <title>DEV Community: Fareez Ahmed</title>
      <link>https://dev.to/fareez1234</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fareez1234"/>
    <language>en</language>
    <item>
      <title>Astro, explained by someone who just built a site with it</title>
      <dc:creator>Fareez Ahmed</dc:creator>
      <pubDate>Fri, 06 Nov 2026 12:35:46 +0000</pubDate>
      <link>https://dev.to/fareez1234/astro-explained-by-someone-who-just-built-a-site-with-it-40nd</link>
      <guid>https://dev.to/fareez1234/astro-explained-by-someone-who-just-built-a-site-with-it-40nd</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%2Fj7ybb4yc77clxum80psd.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%2Fj7ybb4yc77clxum80psd.png" alt="Astro Field Notes cover: a grid of static page blocks with one lit island" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What Astro is, why it ships no JavaScript unless you ask, how islands and content collections actually work, what changed in Astro 7, and the blog I built to find out.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is also a post on the site this repository builds; every code sample comes from the repository.&lt;/p&gt;

&lt;p&gt;Most of the web is documents. Someone lands on a page, reads it, maybe clicks a thing, and leaves. For a good ten years we built those documents with tools made for applications, and the person reading paid for it with a megabyte of JavaScript they never needed. I did this too. Everyone did.&lt;/p&gt;

&lt;p&gt;Astro is a framework built on the opposite bet. Its home page calls it "the web framework for content-driven websites", and once you notice that sentence, every design decision in the thing falls out of it. Pages are rendered to HTML on a server or at build time. The browser gets no JavaScript unless a specific component asks for it. When you do want something interactive, you add it in small, fenced-off pieces called islands, and you can write those in React, Vue, Svelte, Solid, Preact or plain web components, on the same page if you're feeling perverse.&lt;/p&gt;

&lt;p&gt;I wanted to understand it properly, so I built a blog with it. Not a toy: content collections with a schema, one React island, Tailwind, RSS, a sitemap, five themes, and a section that pulls posts out of a real WordPress site. The whole thing is on GitHub and every code sample below comes from it, so if something here looks too neat, you can go and check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem it's solving
&lt;/h2&gt;

&lt;p&gt;A single-page app ships a bundle that builds the page in the browser. Next.js and Nuxt improved on that with server rendering, but they still hydrate the whole page: download the framework, download the component code, download the data, then re-run the render to bolt event handlers onto HTML the browser already has. For a dashboard that's a fair deal. For a blog post it's just a tax.&lt;/p&gt;

&lt;p&gt;You can see it in Core Web Vitals. Astro's home page shows a chart from the HTTP Archive and the Chrome UX Report: the share of real sites that pass all three vitals. Astro sits at 71 percent. WordPress is at 48, Gatsby 45, Next.js 35, Nuxt 28.&lt;/p&gt;

&lt;p&gt;I'd treat those numbers with some care. They're population statistics, not a benchmark, and a good team can make any of those frameworks fast. But most sites aren't built by a good team with time on their hands. Most sites run on defaults. Astro's default is to send HTML and CSS and stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The component format
&lt;/h2&gt;

&lt;p&gt;An Astro component is an HTML file with a fenced block of JavaScript at the top. The fence runs on the server, never in the browser, so you can &lt;code&gt;await&lt;/code&gt; a database query or a &lt;code&gt;fetch&lt;/code&gt; right there and nobody downloads it. Under the fence is HTML with JSX-ish expressions. Here's the post card from my site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
// src/components/PostCard.astro
import type { Post } from '../lib/posts';
import { formatDate, readingTime } from '../lib/posts';
import { url } from '../lib/url';

interface Props {
  post: Post;
}
const { post } = Astro.props;
---

&amp;lt;article class="rounded-card border p-5"&amp;gt;
  &amp;lt;p class="text-xs text-ink-muted"&amp;gt;
    &amp;lt;time datetime={post.data.pubDate.toISOString()}&amp;gt;{formatDate(post.data.pubDate)}&amp;lt;/time&amp;gt;
    &amp;lt;span&amp;gt;{readingTime(post.body)} min read&amp;lt;/span&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;h2&amp;gt;&amp;lt;a href={url(`/blog/${post.id}`)}&amp;gt;{post.data.title}&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;{post.data.description}&amp;lt;/p&amp;gt;
&amp;lt;/article&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things I appreciated. &lt;code&gt;Astro.props&lt;/code&gt; is typed by that &lt;code&gt;Props&lt;/code&gt; interface, so if a page passes the wrong shape the type checker catches it before I do. The template is HTML, not JSX: it's &lt;code&gt;class&lt;/code&gt;, not &lt;code&gt;className&lt;/code&gt;, and you don't need to wrap siblings in a fragment. And the output of this component is a string. There's no runtime for it in the browser because there's nothing left to run.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag inside a component is scoped to that component with no configuration. I went with Tailwind for this site, but scoped styles are the default and they're good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Islands
&lt;/h2&gt;

&lt;p&gt;Etsy's Katie Sylor-Miller coined "component islands" in 2019 and Jason Miller from the Preact team wrote it up in 2020. Astro took the idea and built its whole component model around it. The page is a static sea of HTML. An island is one component that gets hydrated in the browser, on its own, without dragging the rest of the page with it.&lt;/p&gt;

&lt;p&gt;You make an island by adding a &lt;code&gt;client:*&lt;/code&gt; directive to a component. The directive is really a loading strategy. &lt;code&gt;client:load&lt;/code&gt; hydrates straight away, for things the visitor will use immediately. &lt;code&gt;client:idle&lt;/code&gt; waits until the browser has nothing better to do. &lt;code&gt;client:visible&lt;/code&gt; waits until the component scrolls into view, which is what you want for a comments widget at the bottom of a long post. &lt;code&gt;client:media&lt;/code&gt; takes a media query, for a mobile-only menu. And &lt;code&gt;client:only&lt;/code&gt; skips server rendering entirely, for a component that can't run on the server because it reaches for &lt;code&gt;window&lt;/code&gt; during render; use that one sparingly because you lose the HTML.&lt;/p&gt;

&lt;p&gt;The blog index on my site has one island, a search box in React:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
// src/pages/blog/index.astro
import PostSearch from '../../components/PostSearch';
import { getPublishedPosts } from '../../lib/posts';

const posts = await getPublishedPosts();
const searchable = posts.map((post) =&amp;gt; ({
  id: post.id,
  url: url(`/blog/${post.id}`),
  title: post.data.title,
  description: post.data.description,
  tags: post.data.tags,
}));
---
&amp;lt;PostSearch client:load posts={searchable} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take the directive off and Astro still renders the component, to HTML, on the server, and ships no React at all. Put it back and the browser downloads React plus this one component and hydrates it in place. The header, the footer, the layout: all still inert HTML. One rule to remember is that props have to be serialisable, because Astro writes them into the page as JSON. That's why the page flattens the collection entries into plain objects first. I tripped over that once and never again.&lt;/p&gt;

&lt;p&gt;There's a mirror image called server islands. Mark a component &lt;code&gt;server:defer&lt;/code&gt; and it's left out of the cached page and fetched separately on request, with fallback content in its slot in the meantime. It's for the "signed in as" widget on a page you'd otherwise like to cache for a week. It needs on-demand rendering, so it needs an adapter; client islands work on a fully static build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content collections, or the part that makes this a CMS
&lt;/h2&gt;

&lt;p&gt;This is the feature that made me take Astro seriously as a WordPress alternative for sites run by developers. A collection is a folder of content plus a schema. Astro validates every file against the schema when it builds and generates TypeScript types from it.&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="c1"&gt;// src/content.config.ts&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;defineCollection&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;astro:content&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;glob&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;astro/loaders&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;z&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;astro/zod&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;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/content/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.{md,mdx}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;updatedDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;([]),&lt;/span&gt;
    &lt;span class="na"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;featured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;glob&lt;/code&gt; loader reads files off disk. Since Astro 5 the loader is pluggable, so it can just as easily fetch from a REST API, a headless CMS, a database or a CSV, and what comes back is still a typed collection with the same two-function query API:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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;astro:content&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;posts&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;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&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="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&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;draft&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;post&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;getEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&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;astro-explained&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headings&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&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;post.data.pubDate&lt;/code&gt; is a real &lt;code&gt;Date&lt;/code&gt;, not a string, because the schema coerced it. Misspell &lt;code&gt;pubDate&lt;/code&gt; in a Markdown file and the build fails and tells you the file and the field. I've worked with content systems that let that through to production and I'd rather not again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routes, static and otherwise
&lt;/h2&gt;

&lt;p&gt;Routes are files. &lt;code&gt;src/pages/about.astro&lt;/code&gt; is &lt;code&gt;/about&lt;/code&gt;. A file with brackets in the name is a dynamic route, and for a static build it exports a function listing the pages that exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
// src/pages/blog/[id].astro
export async function getStaticPaths() {
  const posts = await getPublishedPosts();
  return posts.map((post, index) =&amp;gt; ({
    params: { id: post.id },
    props: { post, newer: posts[index - 1], older: posts[index + 1] },
  }));
}
const { post, newer, older } = Astro.props;
const { Content } = await render(post);
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default every route is prerendered at build time to an HTML file. If a page genuinely needs per-request data, you add an adapter for your host (&lt;code&gt;npx astro add node&lt;/code&gt;, or &lt;code&gt;vercel&lt;/code&gt;, &lt;code&gt;netlify&lt;/code&gt;, &lt;code&gt;cloudflare&lt;/code&gt;) and set &lt;code&gt;export const prerender = false&lt;/code&gt; on that one page. The two modes mix in one project without fuss. There's also middleware, API endpoints (any file in &lt;code&gt;src/pages&lt;/code&gt; that exports &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;POST&lt;/code&gt;), typed form actions and sessions, none of which my blog needed, which is sort of the point.&lt;/p&gt;

&lt;p&gt;The RSS feed is an endpoint:&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="c1"&gt;// src/pages/rss.xml.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;rss&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;@astrojs/rss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIContext&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;posts&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;getPublishedPosts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rss&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Astro Field Notes&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;A developer blog about Astro.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&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;title&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="nx"&gt;post&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;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&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;pubDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="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;h2&gt;
  
  
  Pulling content from WordPress
&lt;/h2&gt;

&lt;p&gt;Astro doesn't care where content comes from, and I wanted to prove that with the most common CMS there is. The &lt;code&gt;/wordpress&lt;/code&gt; section of my site fetches posts from a WordPress site's REST API during the build and writes a static page for each. The editors keep WordPress. The visitors get HTML off a CDN.&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="c1"&gt;// src/lib/wordpress.ts (abridged)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt; &lt;span class="o"&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;meta&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;WP_API_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://public-api.wordpress.com/wp/v2/sites/en.blog.wordpress.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;FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id,slug,link,date,title,excerpt,content,_links,_embedded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cache&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="nx"&gt;WpPost&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWpPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;perPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&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="nx"&gt;WpPost&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="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&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="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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/posts?per_page=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;perPage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;_embed=1&amp;amp;_fields=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FIELDS&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&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="k"&gt;return &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;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="kd"&gt;as &lt;/span&gt;&lt;span class="nx"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;);&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;cache&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;Two things in there are worth stealing. The promise is cached at module level, so the index page and all the post pages share one request instead of hammering the API. And the real version wraps the fetch in a try/catch that returns an empty list on failure, because I'd rather an unreachable CMS produced an empty section than a failed deploy at eleven at night.&lt;/p&gt;

&lt;p&gt;The pages that consume it look like any other page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
// src/pages/wordpress/[slug].astro
export async function getStaticPaths() {
  const posts = await getWpPosts();
  return posts.map((post) =&amp;gt; ({ params: { slug: post.slug }, props: { post } }));
}
const { post } = Astro.props;
---
&amp;lt;h1 set:html={post.title} /&amp;gt;
&amp;lt;div class="prose" set:html={post.content} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;set:html&lt;/code&gt; injects HTML as-is. WordPress returns HTML for titles and bodies, so it's the right tool, with the usual caveat that you only do this with content you trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling, images, the SEO stuff
&lt;/h2&gt;

&lt;p&gt;Scoped styles, global CSS, CSS modules, Sass and Tailwind all work. Since Astro 5.2 you add Tailwind 4 with &lt;code&gt;npx astro add tailwind&lt;/code&gt;, which installs the official Vite plugin and drops an &lt;code&gt;@import "tailwindcss"&lt;/code&gt; into a global stylesheet. There's no &lt;code&gt;tailwind.config.js&lt;/code&gt; anymore; Tailwind 4 configures itself in CSS, which is how I ended up with themes as blocks of CSS variables.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Picture /&amp;gt;&lt;/code&gt; components from &lt;code&gt;astro:assets&lt;/code&gt; resize and convert images at build time and write out &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; so nothing jumps around while loading. Astro 5.7 brought in a fonts API, experimental at first, for self-hosting web fonts the same way.&lt;/p&gt;

&lt;p&gt;For SEO I installed nothing. The layout sets a canonical URL from &lt;code&gt;Astro.site&lt;/code&gt; and &lt;code&gt;Astro.url&lt;/code&gt;, adds Open Graph and Twitter tags, and links the feed and the sitemap. The sitemap integration writes &lt;code&gt;sitemap-index.xml&lt;/code&gt; from the list of pages it just built.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in Astro 7
&lt;/h2&gt;

&lt;p&gt;Astro 7.0 came out on 22 June 2026 and 7.4, the current release, on 15 October. The whole line is about build speed and a tighter toolchain, and I noticed it: my 45-page site builds in about four seconds.&lt;/p&gt;

&lt;p&gt;Vite 8 with Rolldown replaced esbuild and Rollup, and the Astro team quotes production builds 15 to 61 percent faster; their own site went from 62.7 seconds to 24.2. The &lt;code&gt;.astro&lt;/code&gt; compiler was rewritten in Rust on top of oxc and Lightning CSS. It's quicker and it's stricter: the old compiler would quietly fix malformed HTML, the new one reports an unclosed tag as an error, which bit me once and was right to. Markdown now goes through Sätteri, a Rust pipeline, with the old remark stack still available if you depend on remark plugins. Queued rendering became the default engine. Route caching graduated from experimental, with &lt;code&gt;Astro.cache&lt;/code&gt;, a memory provider and route rules. And there's tooling clearly aimed at CI and agents: &lt;code&gt;astro dev --background&lt;/code&gt;, JSON logs, and in 7.4 an &lt;code&gt;astro build --timings&lt;/code&gt; flag that shows where the time goes. You need Node 22.22 or newer.&lt;/p&gt;

&lt;p&gt;If you're on an older major, &lt;code&gt;npx @astrojs/upgrade&lt;/code&gt; walks you through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I wouldn't use it
&lt;/h2&gt;

&lt;p&gt;Astro's own docs are upfront that it's for content, and I'd agree with where they draw the line.&lt;/p&gt;

&lt;p&gt;If your thing is an application, where every control reacts to every other control, you'd end up with one giant island and none of the benefit. Use Next, Nuxt, SvelteKit or a plain SPA and don't feel bad about it.&lt;/p&gt;

&lt;p&gt;If the people writing content need a visual editor, previews and a plugin store, Astro has no admin UI. Pair it with a headless CMS, keep WordPress as the editor, or just stay on WordPress.&lt;/p&gt;

&lt;p&gt;And if you've got an enormous site with personalisation on every page, you can do it with on-demand rendering and server islands, but at that point you're running a server and you should compare Astro against the application frameworks on their own ground.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, can it replace WordPress?
&lt;/h2&gt;

&lt;p&gt;For a developer-run blog, docs site or marketing site: yes, and the result is faster, close to free to host, and much harder to break into, because there's no server-side runtime on the public internet. For a team that lives in the WordPress editor, I'd keep WordPress as a headless source and let Astro render the front, which is what the demo section on my site does. For sites that are really a plugin ecosystem, WooCommerce and memberships and all that, WordPress still earns its keep. There's a longer post on the site that goes through each case, and another one that's a step-by-step guide to using this project for exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/fareezahmed/astro-blog
&lt;span class="nb"&gt;cd &lt;/span&gt;astro-blog
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;localhost:4321&lt;/code&gt;, edit a file in &lt;code&gt;src/content/blog&lt;/code&gt;, watch it update. Then run &lt;code&gt;npm run build&lt;/code&gt; and poke around in &lt;code&gt;dist/&lt;/code&gt;. Every post is an HTML file. The blog index has one small script for the search box. Nothing else runs in the browser. That folder is the entire site, and anything that can serve files can serve it.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>web</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Use Chromatic to Improve Frontend Development: A Practical Guide</title>
      <dc:creator>Fareez Ahmed</dc:creator>
      <pubDate>Fri, 20 Sep 2024 06:18:53 +0000</pubDate>
      <link>https://dev.to/fareez1234/how-to-use-chromatic-to-improve-frontend-development-a-practical-guide-k1m</link>
      <guid>https://dev.to/fareez1234/how-to-use-chromatic-to-improve-frontend-development-a-practical-guide-k1m</guid>
      <description>&lt;p&gt;In the rapidly changing environment of frontend development, it is extremely important to maintain consistent design, not allowing visual regressions to appear, and to make sure that the quality through different components is still in place. With aim, we added Chromatic, a next-level product that was developed for Storybook, to our development lifecycle. Chromatic gives an effortless way to cope up with this situation by incorporating automated visual testing and review workflows directly into your CI/CD pipeline. &lt;/p&gt;

&lt;p&gt;This guide is based on my experience with Chromatic and exploring its workability in the software development with focus to frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro to Chromatic?
&lt;/h2&gt;

&lt;p&gt;Chromatic is a visual testing and UI review tool built specifically for Storybook, the popular open-source tool for developing and documenting UI components. Chromatic helps you ensure that your UI remains visually consistent by detecting changes in your components over time. Its key features include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Visual Testing:&lt;/strong&gt; Detect visual changes in your components to prevent unintentional regressions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI Review Workflow:&lt;/strong&gt; Team members can review, comment, and approve UI changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snapshot Management:&lt;/strong&gt; Automatically capture snapshots of your components and compare them against the previous versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we Use Chromatic?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To avoid Visual Regressions:&lt;/strong&gt; Chromatic takes snapshots of UIs while they are being developed and immediately alarms you in case any unwanted alterations are found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed up the Time for Review:&lt;/strong&gt; Your team can highlight the visual changes they wish to discuss on a pull request (and these can now be instantly reviewed), so they can thus save on time during the pull request process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decrease Risks Due to Confusion:&lt;/strong&gt; With the help of the automated testing and visual comparisons, developers can now safely introduce new code into the mainline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration:&lt;/strong&gt; By allowing designer, product manager, and other non-technical stakeholders to review and give comments on visual changes that are being made, Chromatic is also a tool for improving the synergy of teams from different departments/units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Chromatic
&lt;/h2&gt;

&lt;p&gt;Here are the practical steps to implement Chromatic in your development workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set Up Storybook
&lt;/h3&gt;

&lt;p&gt;Chromatic works hand-in-hand with Storybook. If you haven't set up Storybook yet, you'll need to start by installing it for your project.&lt;/p&gt;

&lt;p&gt;Install Storybook: Run the following command in your project directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx sb init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Start Storybook:&lt;/em&gt; After installation, you can start Storybook with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run storybook&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Add Components:&lt;/em&gt; Once Storybook is up and running, add stories for the components you want to visually test.&lt;/p&gt;

&lt;p&gt;Example Button.stories.js:&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;React&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;react&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;Button&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;./Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&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/Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Button&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;Template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="nx"&gt;Primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;primary&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;h3&gt;
  
  
  Step 2: Install Chromatic
&lt;/h3&gt;

&lt;p&gt;With Storybook set up, you can now integrate Chromatic into your project.&lt;/p&gt;

&lt;p&gt;Install Chromatic CLI: Install Chromatic as a dev dependency:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save-dev chromatic&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add Chromatic Script: Update your &lt;code&gt;package.json&lt;/code&gt; to include a script for Chromatic. This will run Chromatic to capture your Storybook components:&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;"scripts"&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;"chromatic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chromatic --project-token &amp;lt;your-token&amp;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;The project-token is a unique identifier for your Chromatic project. You can obtain it by signing up on the Chromatic website and creating a new project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Publish Storybook to Chromatic
&lt;/h3&gt;

&lt;p&gt;Next, publish your Storybook to Chromatic for visual testing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Run Chromatic:&lt;/em&gt; Once you've set up the chromatic script, you can run the following command to publish your Storybook:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run chromatic&lt;/code&gt;&lt;br&gt;
This command captures the current state of your Storybook and uploads it to Chromatic's cloud service. Chromatic will generate visual snapshots of each component and compare them to previous versions (if available).&lt;/p&gt;

&lt;h2&gt;
  
  
  My Approach Process using Chromatic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1:Set Up Visual Regression Testing using “Build” Section
&lt;/h3&gt;

&lt;p&gt;After the Storybook is uploaded, Chromatic will start automatically comparing new component snapshots with older versions to detect visual regressions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Baseline Snapshots:&lt;/strong&gt; On the first run, Chromatic captures baseline snapshots. These are used as the standard to compare against in future builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review UI Changes:&lt;/strong&gt; Chromatic provides an interface where you can review any detected changes. You can accept changes, flag regressions, or ask your team to weigh in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approve or Reject Changes:&lt;/strong&gt; Team members can approve visual changes directly from Chromatic’s UI or integrate approvals as part of your pull request process using GitHub, GitLab, or Bitbucket integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Review Visual Changes in Pull Requests using “Review” Section
&lt;/h3&gt;

&lt;p&gt;Chromatic integrates with platforms like GitHub, GitLab, and Bitbucket to display visual diffs directly in your pull requests. This means developers can see side-by-side comparisons of component changes before merging code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a Pull Request:&lt;/strong&gt; When a pull request is created, Chromatic automatically runs visual tests and reports any changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approve or Comment:&lt;/strong&gt; Team members can approve, request changes, or comment on specific UI snapshots directly from the Chromatic interface within the pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: House Keeping by manage Snapshot Versions
&lt;/h3&gt;

&lt;p&gt;As your project develops, manage and organize snapshots more effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snapshot Branching:&lt;/strong&gt; Chromatic supports snapshot branching, meaning it can compare snapshots from different branches. This is useful for larger teams working on multiple features concurrently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean Up Old Snapshots:&lt;/strong&gt; Over time, you might accumulate old snapshots. Chromatic provides tools for managing and deleting outdated snapshots to keep your workspace clean.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
