<?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: Aliaksandr Bandziuk</title>
    <description>The latest articles on DEV Community by Aliaksandr Bandziuk (@aliaksandrbandziuk).</description>
    <link>https://dev.to/aliaksandrbandziuk</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%2F1389926%2Ff0705615-393c-4b7b-bbe6-c680d6e53883.jpeg</url>
      <title>DEV Community: Aliaksandr Bandziuk</title>
      <link>https://dev.to/aliaksandrbandziuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aliaksandrbandziuk"/>
    <language>en</language>
    <item>
      <title>Building a site for AI answers in Next.js 16 — what actually made a difference</title>
      <dc:creator>Aliaksandr Bandziuk</dc:creator>
      <pubDate>Mon, 21 Sep 2026 13:43:50 +0000</pubDate>
      <link>https://dev.to/aliaksandrbandziuk/building-a-site-for-ai-answers-in-nextjs-16-what-actually-made-a-difference-2979</link>
      <guid>https://dev.to/aliaksandrbandziuk/building-a-site-for-ai-answers-in-nextjs-16-what-actually-made-a-difference-2979</guid>
      <description>&lt;p&gt;Before I designed a website for a retail analytics consultant, I checked what people see when they search her field. Of 40 English queries, &lt;strong&gt;38 showed an AI answer at the top of the page&lt;/strong&gt;. In Polish it was 35 of 37, in Russian 36 of 36.&lt;/p&gt;

&lt;p&gt;That changes the job. Ranking in a list of links is no longer the goal; being the page the answer quotes is. Most of what follows is about making a Next.js site easy to read and easy to quote for crawlers that — this is the important part — &lt;strong&gt;mostly do not run JavaScript&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The site: &lt;a href="https://www.tatsianabandziuk.com" rel="noopener noreferrer"&gt;tatsianabandziuk.com&lt;/a&gt; — 153 pages in three languages, built on Next.js 16 (App Router, Turbopack, React 19) with Sanity as the CMS. The full case study with screenshots is &lt;a href="https://www.bandziuk.com/portfolio/consultant-website-design-retail-analytics?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=tatsianabandziuk-case&amp;amp;utm_content=case-link" rel="noopener noreferrer"&gt;on my site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. If it needs JavaScript, AI crawlers don't see it
&lt;/h2&gt;

&lt;p&gt;Google renders JavaScript. Most of the fetchers behind AI answers don't. So everything that carries meaning has to be in the server HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FAQ answers are collapsed with CSS, not fetched on click.&lt;/strong&gt; The answer is in the markup from the first byte; the accordion only changes what is visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Figures, menus and the language switcher are server-rendered.&lt;/strong&gt; A number that animates in from zero is fine — as long as the final value is already in the HTML and the animation only rewrites it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charts are drawn on the server.&lt;/strong&gt; Each service card has a small chart; it is SVG in the HTML, not a canvas painted after hydration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The quick test is &lt;code&gt;curl&lt;/code&gt; against your own page and a search for the text you expect an assistant to quote. If it isn't in the output, it doesn't exist for most of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. JSON-LD: a plain &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, not &lt;code&gt;next/script&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This one cost me time on another project, so it's the first thing I check now. &lt;code&gt;next/script&lt;/code&gt; — with or without &lt;code&gt;strategy="beforeInteractive"&lt;/code&gt; — does not put the JSON-LD into the server HTML in the App Router. It queues it and injects it after hydration. Google will eventually see it; a fetcher that doesn't run JS never will.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Do this&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;
  &lt;span class="na"&gt;suppressHydrationWarning&lt;/span&gt;
  &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&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;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Not this — the block lands in the page only after hydration&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Script&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"schema"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&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;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structured data itself is written by hand for twelve page types. At its core are three nodes — the person, her services, the website — each with a stable &lt;code&gt;@id&lt;/code&gt;, and every page references them instead of restating them. A calculator page is described as a free tool, a case study and an article as publications with an author and a date, and the About page lists each diploma with its year and issuer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Write for quoting, not just for reading
&lt;/h2&gt;

&lt;p&gt;This is content work, but it has a code side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The answer is the first sentence&lt;/strong&gt; of every article and calculator page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formulas and tables are text.&lt;/strong&gt; A formula in an image cannot be quoted. On the calculator pages the formula sits in its own element as text, and styled tables are still plain &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; underneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headings stand alone.&lt;/strong&gt; Each one names its subject fully, so it still makes sense when an assistant lifts it out of the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Let the AI crawlers in, keep the data scrapers out
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;robots.ts&lt;/code&gt; allows the search engines and the AI crawlers — GPTBot, ClaudeBot, PerplexityBot and friends — and disallows the commercial SEO crawlers that collect link data for other people's tools. They add load and bring neither readers nor citations.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Caching that doesn't fight the CMS
&lt;/h2&gt;

&lt;p&gt;Every Sanity query goes through one small wrapper that adds a cache lifetime and a tag:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;R&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;query&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="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&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;R&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;return&lt;/span&gt; &lt;span class="nx"&gt;sanityClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;R&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;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity&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="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 reasons it exists. First, the Sanity client sends an &lt;code&gt;Authorization&lt;/code&gt; header (translated documents are private), and without an explicit cache lifetime Next treats that fetch as dynamic — the whole route renders on every request. Second, pages can then be cached for a day and still update on publish: a Sanity webhook calls &lt;code&gt;revalidateTag("sanity")&lt;/code&gt;, and the same webhook pings IndexNow for the changed URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Next 16 gotcha worth knowing:&lt;/strong&gt; &lt;code&gt;revalidateTag&lt;/code&gt; now takes a cache profile. On a different project I used &lt;code&gt;{ expire: 0 }&lt;/code&gt;, and every route with &lt;code&gt;dynamicParams = false&lt;/code&gt; started answering 404 after a publish — the cached pages were deleted and those routes can't regenerate. The &lt;code&gt;"max"&lt;/code&gt; profile marks them stale instead: the first request after a publish gets the old page and triggers regeneration, the next one gets fresh data. If you have &lt;code&gt;dynamicParams = false&lt;/code&gt; anywhere, test your publish webhook against a real build.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. PageSpeed 100 on mobile — the unglamorous parts
&lt;/h2&gt;

&lt;p&gt;Mobile PageSpeed Insights gives the site 100 for performance, accessibility, best practices and SEO, and 3 of 3 in the new agentic browsing check. What moved the needle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No font preloading.&lt;/strong&gt; &lt;code&gt;preload: false&lt;/code&gt; with &lt;code&gt;display: "swap"&lt;/code&gt; on every &lt;code&gt;next/font&lt;/code&gt; family. Lighthouse's simulation puts every request that starts before the largest paint on its critical path, and preloaded fonts landed there. The H1 paints in the metric-matched fallback and swaps a moment later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next's polyfill module aliased to an empty file&lt;/strong&gt; through &lt;code&gt;turbopack.resolveAlias&lt;/code&gt; — every browser Next 16 supports already has those features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images resized by Sanity's CDN&lt;/strong&gt; through a custom loader rather than &lt;code&gt;/_next/image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An &lt;code&gt;/llms.txt&lt;/code&gt;&lt;/strong&gt; built from the same content as the pages, which is what the agentic check looks for.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. The site keeps building when the CMS doesn't answer
&lt;/h2&gt;

&lt;p&gt;All content has a fallback copy inside the repository, so a Sanity outage during a build produces the last known pages instead of a failed deploy. And the calculators and the downloadable Excel templates are verified by scripts — the template checker recalculates the whole workbook and compares every formula with the expected result. A template that is wrong by one cell reference is worse than no template.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take to the next project
&lt;/h2&gt;

&lt;p&gt;Check the results page before the design: if an AI answer sits on top of your target searches, write and build to be quoted. Then run &lt;code&gt;curl&lt;/code&gt; on your own pages and read what a crawler without JavaScript actually receives. Most of the fixes above came from that one command.&lt;/p&gt;

&lt;p&gt;I measured AI citations on launch day — zero, as expected for a new site — and will re-run the same 74 questions in October.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm Aliaksandr Bandziuk, an SEO consultant and web developer building multilingual sites on Next.js and headless CMS: &lt;a href="https://www.bandziuk.com/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=tatsianabandziuk-case&amp;amp;utm_content=bio" rel="noopener noreferrer"&gt;bandziuk.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>seo</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Every AI assistant reads a different web. I counted the citations in 141 answers</title>
      <dc:creator>Aliaksandr Bandziuk</dc:creator>
      <pubDate>Mon, 14 Sep 2026 10:37:45 +0000</pubDate>
      <link>https://dev.to/aliaksandrbandziuk/every-ai-assistant-reads-a-different-web-i-counted-the-citations-in-141-answers-387c</link>
      <guid>https://dev.to/aliaksandrbandziuk/every-ai-assistant-reads-a-different-web-i-counted-the-citations-in-141-answers-387c</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57fy8fg0pugu8hrhfij1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57fy8fg0pugu8hrhfij1.webp" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I put the same 47 buying questions to ChatGPT, Perplexity and Google AI Mode on the same day, and classified every citation across the resulting 141 answers. The engineering-relevant part of the result is not the ranking. It is how differently the three engines behave as clients of your site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citations per answer.&lt;/strong&gt; Perplexity 16.7, Google AI Mode 3.7, ChatGPT 1.6. Perplexity cited provider websites 667 times across its 47 answers. ChatGPT cited them 47 times, almost exactly one per answer.&lt;/p&gt;

&lt;p&gt;That gap is the whole engineering brief. Against Perplexity, the page you ship is genuinely in play: it reads widely and shows its work. Against ChatGPT, at roughly one site per answer, selection happens before your page is read, and no amount of on-page work changes the shortlist you were not on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A trap worth naming.&lt;/strong&gt; ChatGPT referenced Google Maps in 23 of 47 answers, which reads as an argument for a verified business listing. It is not. All 507 Maps links were of the form &lt;code&gt;google.com/maps/search/Name,+City&lt;/code&gt;, a constructed query. Links to actual listings, &lt;code&gt;/maps/place/&lt;/code&gt;, numbered zero. The model finds the provider by ordinary web search and renders the map link as an affordance. Check link shapes before drawing a conclusion from a hostname.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One implementation detail that surprised me while checking my own site.&lt;/strong&gt; JSON-LD rendered through Next.js &lt;code&gt;next/script&lt;/code&gt; does not appear in the server HTML at all, including with &lt;code&gt;strategy="beforeInteractive"&lt;/code&gt;. The content is queued into &lt;code&gt;self.__next_s&lt;/code&gt; and injected after hydration. Google executes JavaScript and gets there eventually. Most of the fetchers behind assistant answers do not. On one page I could see it directly: a &lt;code&gt;FAQPage&lt;/code&gt; block emitted through a plain &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag showed up in &lt;code&gt;curl&lt;/code&gt; output, while a &lt;code&gt;BlogPosting&lt;/code&gt; block emitted through &lt;code&gt;next/script&lt;/code&gt; on the same page did not.&lt;/p&gt;

&lt;p&gt;If you are marking up a site for this, use a plain &lt;code&gt;&amp;lt;script type="application/ld+json" suppressHydrationWarning&amp;gt;&lt;/code&gt;. It is a one-line difference and it decides whether your structured data exists for non-JS readers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The channel split.&lt;/strong&gt; The Google results page for these commercial terms is roughly half listicles and half agency landing pages. Assistant answers on the same subject lean on video, professional networks, forums and established publishers. Optimising for one does not move the other.&lt;/p&gt;

&lt;p&gt;Full method, all the tables, the sample size caveats and a section on what the data does not support: &lt;a href="https://www.bandziuk.com/blog/ai-assistant-recommendations-study" rel="noopener noreferrer"&gt;https://www.bandziuk.com/blog/ai-assistant-recommendations-study&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Does your site need an llms.txt file? What the data actually shows</title>
      <dc:creator>Aliaksandr Bandziuk</dc:creator>
      <pubDate>Wed, 02 Sep 2026 13:45:27 +0000</pubDate>
      <link>https://dev.to/aliaksandrbandziuk/does-your-site-need-an-llmstxt-file-what-the-data-actually-shows-59e5</link>
      <guid>https://dev.to/aliaksandrbandziuk/does-your-site-need-an-llmstxt-file-what-the-data-actually-shows-59e5</guid>
      <description>&lt;p&gt;If you're adding llms.txt hoping to get cited more often by ChatGPT or Google's AI answers, the evidence says it won't do that. But the file isn't useless either — it just does a different job than most people think, and a few major AI companies genuinely use it for that job.&lt;/p&gt;

&lt;p&gt;Here's what I found when I went looking for actual measurements rather than opinions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the file is
&lt;/h2&gt;

&lt;p&gt;A markdown file at your domain root containing a curated map of your site — the pages that matter, with short descriptions, organised so a machine can see the structure without crawling everything.&lt;/p&gt;

&lt;p&gt;The proposal dates from September 2024. The reasoning is sound: a large documentation site has thousands of pages, and a system reading it without guidance may index outdated material and misread the structure. A curated index gives it a better starting point.&lt;/p&gt;

&lt;p&gt;Note what that describes. It's a navigation aid — not access control, not a ranking signal. It can't block any crawler.&lt;/p&gt;

&lt;h2&gt;
  
  
  The citation claim doesn't hold up
&lt;/h2&gt;

&lt;p&gt;Google's Search Central documentation states it directly: Google Search does not use llms.txt. Not for rankings, not for AI Overviews, not at all. That position has been consistent since Gary Illyes confirmed it in July 2025.&lt;/p&gt;

&lt;p&gt;Independent measurement points the same way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Across hundreds of thousands of domains, 97% of llms.txt files were never fetched by anything&lt;/li&gt;
&lt;li&gt;One monitoring exercise covering 500M+ AI bot visits over 90 days recorded 408 requests targeting llms.txt directly&lt;/li&gt;
&lt;li&gt;Statistical models looking for a citation effect found none&lt;/li&gt;
&lt;li&gt;A study of 300,000 domains put implementation at just over 10%, eighteen months into the conversation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;John Mueller compared llms.txt to the keywords meta tag, and the comparison is worth taking seriously rather than as a dismissal.&lt;/p&gt;

&lt;p&gt;The keywords tag failed for a specific reason: it was a self-declaration nobody could verify. A site said what it was about, and nothing stopped it from saying whatever was convenient.&lt;/p&gt;

&lt;p&gt;llms.txt has the same shape. It's a file in which a site describes its own importance, with no verification anywhere in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  But several AI companies do use it
&lt;/h2&gt;

&lt;p&gt;This is where "useless" overshoots.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic recommends it in its guidance on writing for agents&lt;/li&gt;
&lt;li&gt;OpenAI maintains llms.txt files for its Agents SDK&lt;/li&gt;
&lt;li&gt;Perplexity has said it retrieves the file to help prioritise which pages to read&lt;/li&gt;
&lt;li&gt;Chrome's Lighthouse moved its llms.txt audit out of experimental into a default agentic browsing category in May 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the Chrome team and the Search team are on visibly opposite sides of this.&lt;/p&gt;

&lt;p&gt;The reconciliation: the file serves agentic retrieval, not conversational answering.&lt;/p&gt;

&lt;p&gt;When a developer asks a coding assistant to work against someone's documentation, the assistant can fetch that site's llms.txt as a table of contents and traverse only the pages it needs, rather than scraping the whole site. That workflow is real and constant — and it's invisible to studies measuring crawler behaviour or citation rates, because it's neither crawling nor citation.&lt;/p&gt;

&lt;p&gt;Which explains the adoption pattern precisely. The companies shipping llms.txt are documentation-heavy: developer platforms, infrastructure providers, API companies. Marketing sites and blogs mostly haven't, and that isn't neglect — their audience isn't coding agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  So should you ship one?
&lt;/h2&gt;

&lt;p&gt;Three questions settle it:&lt;/p&gt;

&lt;p&gt;Do agents work against your content? If you publish documentation, an API reference or technical material developers point tools at — yes. The benefit is real for those users whatever the citation studies say.&lt;/p&gt;

&lt;p&gt;Are you adding it to improve AI visibility? Then no, and the effort belongs somewhere with evidence behind it: facts stated so a machine can repeat them, structured data matching your copy, corroboration outside your own site.&lt;/p&gt;

&lt;p&gt;Do you have spare capacity? Then it's defensible. It takes an hour and breaks nothing. Just don't count it as visibility work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The implementation mistake worth avoiding
&lt;/h2&gt;

&lt;p&gt;A popular approach generates a markdown copy of every page alongside the index. If those copies are indexable, you've created duplicate content at scale — every page now exists twice, competing with itself.&lt;/p&gt;

&lt;p&gt;The file is supposed to be a curated index: your important pages, described briefly, linked. Not a second copy of the site. A small site can list twenty entries; a large one should list the twenty that matter rather than all nine hundred.&lt;/p&gt;

&lt;h2&gt;
  
  
  The file that actually controls AI access
&lt;/h2&gt;

&lt;p&gt;Worth separating, because these two get conflated constantly.&lt;/p&gt;

&lt;p&gt;robots.txt has genuine, deliberate backing from every major AI crawler operator. OpenAI, Anthropic, Google, Perplexity and the rest publish user-agent strings and honour the directives.&lt;/p&gt;

&lt;p&gt;llms.txt cannot block anything. It's a suggestion about what to read once reading is already permitted.&lt;/p&gt;

&lt;p&gt;Practical consequence: if AI visibility matters to you, the thing worth checking isn't whether you have an llms.txt file. It's whether your robots.txt is accidentally blocking the crawlers you want. That takes two minutes and it's the one of the two that can actually cost you.&lt;/p&gt;

&lt;p&gt;Has anyone here measured a difference after shipping llms.txt? I've only found studies showing no effect, but those measure crawling and citation — not the agentic retrieval case, which is much harder to observe from outside. Curious whether anyone has data on that side.&lt;/p&gt;

&lt;p&gt;I write about search, AI visibility and multilingual sites at &lt;a href="https://www.bandziuk.com" rel="noopener noreferrer"&gt;bandziuk.com&lt;/a&gt; — this post first appeared there.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>web3</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How a client found me through ChatGPT — and what it taught me about building for AI search</title>
      <dc:creator>Aliaksandr Bandziuk</dc:creator>
      <pubDate>Sat, 25 Jul 2026 17:17:17 +0000</pubDate>
      <link>https://dev.to/aliaksandrbandziuk/how-a-client-found-me-through-chatgpt-and-what-it-taught-me-about-building-for-ai-search-2a2f</link>
      <guid>https://dev.to/aliaksandrbandziuk/how-a-client-found-me-through-chatgpt-and-what-it-taught-me-about-building-for-ai-search-2a2f</guid>
      <description>&lt;p&gt;Most posts about "optimizing for AI search" are written in the future tense: this will matter soon, you should prepare. I have a reason to write in the past tense.&lt;/p&gt;

&lt;p&gt;A client reached out through the form on my site — a detailed message with a clear list of requirements: SEO, AI-search optimization, structured data, Core Web Vitals, accessibility. Two days later we signed a contract. Partway into the work I asked how he'd found me. His answer: he asked ChatGPT who could do this kind of work.&lt;/p&gt;

&lt;p&gt;Not Google. Not a referral. Not an ad.&lt;/p&gt;

&lt;p&gt;That single data point sent me down a rabbit hole, and this post is what I found — including the part where I asked ChatGPT the same kind of question myself and watched it recommend my own site.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI-assistant search actually differs from Google
&lt;/h2&gt;

&lt;p&gt;The difference isn't the interface — it's how people phrase the request.&lt;/p&gt;

&lt;p&gt;In a search box, people compress their need into keywords the system will understand: &lt;strong&gt;&lt;em&gt;next.js developer warsaw&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;freelance technical seo&lt;/em&gt;&lt;/strong&gt;. In a conversation with an assistant, they describe the situation the way they'd tell a friend: who they are, what their business is, the constraints, the stack.&lt;/p&gt;

&lt;p&gt;And the assistant doesn't return ten blue links. It returns a shortlist — two to five options, often with a one-line reason each. The person doesn't scan twenty results; they get three names and go check them.&lt;/p&gt;

&lt;p&gt;Two consequences that change the game:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The list is shorter.&lt;/strong&gt; Organic search has a page 1, 2, 3. An assistant's answer has ~5 slots. You're in it or you don't exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The query is longer and more specific.&lt;/strong&gt; People describe language, country, niche, the exact combination. Winning a broad head term matters less than being the unambiguous answer to a specific combination of constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why an assistant recommends one site over another
&lt;/h2&gt;

&lt;p&gt;An assistant isn't judging who's the best contractor — it has no experience of your work. It assembles an answer from what it could find and confidently parse. So the recommendations don't go to the strongest people on the market; they go to the ones whose specialization a machine can read without guessing.&lt;/p&gt;

&lt;p&gt;In practice that comes down to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unambiguous copy.&lt;/strong&gt; "We craft digital solutions for your growth" is an empty string to a machine. "Website development for psychologists, languages: English, Polish, Russian" is a fact it can match against a query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured data.&lt;/strong&gt; Schema.org markup states, in machine-readable form, what the organization is, what services exist, where it operates, what the FAQs are. Without it the system infers from your DOM. With it, it reads facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency across sources.&lt;/strong&gt; When your name, specialization and details match across your site, profiles and directories, confidence goes up. When they contradict, the assistant is more likely to omit you than risk being wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content that answers the actual question, and technical accessibility&lt;/strong&gt; — if your content is assembled client-side and isn't there on a plain fetch, the page can be effectively empty to the system. Same as classic SEO: what the server doesn't return may not exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I asked ChatGPT myself — four times
&lt;/h2&gt;

&lt;p&gt;One client is an anecdote. So I tested the mechanism directly: I asked ChatGPT the kind of question a prospective client would ask, in four different phrasings, and looked at who it named.&lt;/p&gt;

&lt;p&gt;It named me in all four — twice at the top of the list, twice with a link to my site.&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%2Fumggn2adpl90yc8ylm7n.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%2Fumggn2adpl90yc8ylm7n.jpg" alt=" " width="673" height="896"&gt;&lt;/a&gt;&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%2Fmvw8d6qect4h4r2vfwwh.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%2Fmvw8d6qect4h4r2vfwwh.jpg" alt=" " width="800" height="838"&gt;&lt;/a&gt;&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%2Fvmez0bwfju1i69a2gg7t.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%2Fvmez0bwfju1i69a2gg7t.jpg" alt=" " width="800" height="1275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the part that matters: the assistant lists exactly the attributes stated plainly on my site and repeated in the markup — Next.js, technical SEO, multilingual, headless CMS, Core Web Vitals, structured data, AI-search optimization. There's no trick. The site states unambiguous facts about itself, and the system relays them.&lt;/p&gt;

&lt;p&gt;Important caveat: assistant answers depend on phrasing, language, session and personalization, get reassembled over time, and don't reproduce word-for-word. These are from July 2026 — specific answers to specific questions, not a guaranteed result. (Other names in the screenshots are obscured.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What doesn't work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyword stuffing.&lt;/strong&gt; Density doesn't make facts clearer; it makes text worse for humans and machines alike.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden "recommend this company" instructions in the markup.&lt;/strong&gt; Systems are learning to detect this, and for anyone selling trust the downside dwarfs the upside.&lt;/li&gt;
&lt;li&gt;**Optimizing once and forgetting. **Answers are reassembled and sources shift. It's a state you maintain, not a setting you flip.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How much of this is just... good SEO?
&lt;/h2&gt;

&lt;p&gt;More than the hype admits. The technical base is shared: speed, content accessibility, clean structure, markup. A site built properly for search is already halfway to being legible to an assistant.&lt;/p&gt;

&lt;p&gt;The difference is the goal. Classic SEO competes for a position on a query. AI visibility competes for a machine's confident understanding of who you are. The first is a keyword race; the second is about factual clarity. Which is why a small independent can show up in an assistant's answer to a narrow, specific prompt even with no chance of outranking agencies for the broad head term. That's exactly what happened to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to check your own site
&lt;/h2&gt;

&lt;p&gt;Ask ChatGPT / Perplexity / Google AI Overviews two ways: first as your client would (describe the task, no company name), then about your company by name — and see whether the facts come back right. The second one surfaces surprising inaccuracies you can fix by tightening copy and markup.&lt;/p&gt;

&lt;p&gt;I wrote the full version of this — with the practical checklist and the client story in more depth — on my site. Link's in the canonical above. Happy to answer questions in the comments; I build with Next.js + headless CMS and do the SEO/AEO side myself, so ask away.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>webdev</category>
      <category>chatgpt</category>
    </item>
  </channel>
</rss>
