<?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: Pratik Goswami</title>
    <description>The latest articles on DEV Community by Pratik Goswami (@pratik_goswami).</description>
    <link>https://dev.to/pratik_goswami</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3921707%2Ff3f6b28d-d101-43e7-a345-cd6af573a385.jpg</url>
      <title>DEV Community: Pratik Goswami</title>
      <link>https://dev.to/pratik_goswami</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pratik_goswami"/>
    <language>en</language>
    <item>
      <title>I Read Google’s Official AI Search Guide So You Don’t Have To. Here’s What Actually Matters</title>
      <dc:creator>Pratik Goswami</dc:creator>
      <pubDate>Fri, 22 May 2026 09:28:31 +0000</pubDate>
      <link>https://dev.to/pratik_goswami/i-read-googles-official-ai-search-guide-so-you-dont-have-to-heres-what-actually-matters-1en</link>
      <guid>https://dev.to/pratik_goswami/i-read-googles-official-ai-search-guide-so-you-dont-have-to-heres-what-actually-matters-1en</guid>
      <description>&lt;p&gt;&lt;em&gt;Google just wrapped up its Google I/O 2026 and after spending some time going through all of Google's official documentation on Search Agents, I decided to create a one-stop guide to things I took away from all the information. While I originally posted on Medium, I wanted to share it here as well because the Dev.to community tends to care more about these stuff.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Agentic Search Optimization, Answer Engine Optimization, Generative Engine Optimization - the terminology changes depending on who you follow, but the panic underneath it is the same: &lt;em&gt;Google has changed, agents are reading your site, and if you are not optimizing for it, you are invisible.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So developers have been doing what developers do. They are adding &lt;code&gt;llms.txt&lt;/code&gt; files. They are "chunking" their content. They are rewriting copy specifically for AI models. Some are even paying for tools that promise to get their site cited by ChatGPT.&lt;/p&gt;

&lt;p&gt;Here is the thing. Google just published an official guide on how to optimize your website for generative AI features on Google Search. I read it cover to cover. Most of what the internet is selling you isn’t what search engines are looking for. This article highlights what can actually boost your visibility to such agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, a Quick Recap on Why This Matters
&lt;/h2&gt;

&lt;p&gt;If you read my previous article, &lt;a href="https://medium.com/@prtkgoswami8/your-portfolio-is-invisible-heres-how-i-fixed-mine-193b1f503fc7" rel="noopener noreferrer"&gt;Your Portfolio Is Invisible. Here’s How I Fixed Mine&lt;/a&gt;, feel free to skip this section as you already know the core idea. Your site has two types of readers: humans and machines. And increasingly, it is the machines that decide whether the humans ever find you.&lt;/p&gt;

&lt;p&gt;SEO (Search Engine Optimization) is about helping crawlers like Googlebot index and rank your content so people find you in search results.&lt;/p&gt;

&lt;p&gt;AEO (Answer Engine Optimization) is about helping AI systems form an accurate and confident understanding of your site, so they can surface it when someone asks a relevant question.&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.amazonaws.com%2Fuploads%2Farticles%2Ff7h09r1kcwfxz1zf5t5i.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.amazonaws.com%2Fuploads%2Farticles%2Ff7h09r1kcwfxz1zf5t5i.png" alt="SEO Vs AEO: SEO gets you on the map. AEO makes you a landmark." width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The difference matters more now than it did a year ago. Google is actively building Search Agents. These are autonomous systems that can perform tasks on behalf of users. Whether it be booking a reservation, comparing products or researching services; to do any of that, the agent first has to read, understand, and trust your site. If it cannot do that, it will not recommend you and that is the whole problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Google Actually Says
&lt;/h2&gt;

&lt;p&gt;Google has created a guide titled Optimizing your website for generative AI features on Google Search. It is the official position from Google Search Central and it serves as a guide to optimize a website for the new Search Agents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From Google’s perspective, optimizing for generative AI search is optimizing for the search experience, and thus still SEO.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It means the same foundational principles that have always made a site rank well - original content, clean structure and good performance - are still what matter. The difference is in how those principles are applied in a world where an AI model is summarizing your page rather than a user clicking through to read it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: What Actually Moves the Needle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Non-Commodity Content
&lt;/h3&gt;

&lt;p&gt;This is the section that gets the most emphasis in the Google guide and the least attention in most AEO tutorials.&lt;/p&gt;

&lt;p&gt;Google uses the phrase “valuable, non-commodity content” and it is worth understanding what they mean.&lt;/p&gt;

&lt;p&gt;Commodity content is a summary of information that already exists. It answers a question accurately, but it could have been written by anyone or generated by an AI model. The content has neither a point of view nor a first-hand experience. It does not tell you something you could not find in the first five results for the same query.&lt;/p&gt;

&lt;p&gt;Non-commodity content is the opposite. It can include a first-hand review, case study with real numbers or an expert take that reflects genuine experience in the field. Something that provides a unique viewpoint.&lt;/p&gt;

&lt;p&gt;Here is the uncomfortable truth. The content most developers are cranking out to “rank for AI” is precisely what AI models are trained to ignore. If you are writing blog posts that restate what the documentation already says, you are producing commodity content. The irony is that the shortcut most people reach for, using AI to generate more content faster, tends to produce the exact type of content that generative AI systems are trained to look past.&lt;/p&gt;

&lt;p&gt;Your perspective is the signal. The depth you bring from actually having built, shipped, and debugged something, is what makes your content worth citing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clear Technical Structure
&lt;/h3&gt;

&lt;p&gt;This part will be familiar if you read the SEO article, but the reasoning has a new layer to it now.&lt;/p&gt;

&lt;p&gt;Google’s AI systems use the same underlying infrastructure as traditional search. That means the same technical requirements apply. A page has to be indexed and eligible to appear in Google Search before it can appear in AI Overviews or be used by a Search Agent.&lt;/p&gt;

&lt;p&gt;The checklist looks like this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic HTML&lt;/strong&gt;. This is still the highest-leverage change most developers can make. When your markup uses &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; through &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; correctly, a crawler reads your page like an outline. Without them, it reads a flat document and has to guess what matters. The Google guide specifically notes that while perfectly semantic HTML is not required, it is generally a good idea because it helps multiple types of users, including AI systems that parse and navigate your page structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crawlable content&lt;/strong&gt;. If your content only renders after a user interaction, a state toggle, a tab click, or a modal open, it effectively does not exist to a crawler. Googlebot crawls the initial DOM, not the post-interaction state. Audit your site for content that is conditionally rendered and refactor it so the important information is present in the initial HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sitemap and robots hygiene&lt;/strong&gt;. A sitemap provides a guide to the crawlers about every page on your site so nothing gets missed. A robots file tells crawlers which pages they are and are not allowed to visit. Both are small files that have an outsized impact.&lt;/p&gt;

&lt;p&gt;Good page experience. Google’s guide explicitly links page experience as an AI search signal. Thus, Core Web Vitals like LCP (Largest Contentful Paint), which measures how long it takes for the largest visible element to load, has a major impact. A slow site is not just a UX problem, it is a visibility problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Structured Data (The Right Way to Think About It)
&lt;/h3&gt;

&lt;p&gt;There is a nuance here that most articles miss. While structured data is not required for generative AI search, it is still a good idea to use it responsibly as it is a part of improving the overall SEO strategy. In other words there is no special &lt;em&gt;schema.org&lt;/em&gt; that can give you an edge with AI search, but it does help with being eligible for rich results on Google Search.&lt;/p&gt;

&lt;p&gt;Generative AI search is built on top of the same index as traditional search. A site that is well-optimized for rich results tends to also be better represented in AI responses. Structured data gives crawlers explicit, machine-readable facts about your content rather than making them infer those facts from your prose.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Person&lt;/code&gt; schema on a portfolio tells Google your name, your role, your employer, and your social profiles in one structured block. A &lt;code&gt;Product&lt;/code&gt; schema on an e-commerce page gives price, availability, and reviews in a format that leaves nothing to interpretation.&lt;/p&gt;

&lt;p&gt;For a personal site, the most useful schemas are &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;WebSite&lt;/code&gt;, and &lt;code&gt;SoftwareApplication&lt;/code&gt; if you are showcasing projects. For a commercial site, focus on &lt;code&gt;Organization&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;, &lt;code&gt;FAQPage&lt;/code&gt;, and &lt;code&gt;LocalBusiness&lt;/code&gt; if applicable.&lt;/p&gt;

&lt;p&gt;The goal is not to stuff your page with schema, rather to shine the spotlight on your most important facts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: The Myths Worth Dropping
&lt;/h2&gt;

&lt;p&gt;I want to go through it because these myths are widespread, and I believe acting on them is a waste of time at best, counterproductive at worst.&lt;/p&gt;

&lt;h3&gt;
  
  
  llms.txt is not a signal
&lt;/h3&gt;

&lt;p&gt;This has been circulating as the hot new AEO tactic. The idea is that you create a machine-readable file at your domain root describing your site for LLMs, similar to how &lt;code&gt;robots.txt&lt;/code&gt; works for crawlers. Google’s guide is clear to highlight that you do not need to create &lt;code&gt;llms.txt&lt;/code&gt; or any other "special" markup for AI. While they will be crawled and indexed, they don’t provide any advantage to improving visibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunking content does not help
&lt;/h3&gt;

&lt;p&gt;The theory is that breaking your pages into smaller pieces helps AI systems process them more easily. Google explicitly highlights this as a myth, as their systems can handle multiple topics on a single page and surface the relevant piece to users. There is no ideal page length for generative AI search. So make pages for your audience, not for the AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  You do not need to write for AI keywords
&lt;/h3&gt;

&lt;p&gt;AI systems can understand synonyms. They understand the general meaning of a query and match it to relevant content even when the phrasing does not match. You do not need to worry about capturing every variation of how someone might search for your topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeking inauthentic “mentions” does not work
&lt;/h3&gt;

&lt;p&gt;Some tools and guides push you to get your brand mentioned in blog posts, forums, and discussion threads across the web, with the reasoning that it will help AI systems associate your brand positively.&lt;/p&gt;

&lt;p&gt;Google addresses this directly. Their core ranking systems focus on &lt;strong&gt;high-quality&lt;/strong&gt; content while others focus on &lt;strong&gt;blocking spam&lt;/strong&gt;. Their generative AI feature depends on both. Thus, pushing for bulk mentions may be harming visibility instead of boosting it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: The Part Most Guides Are Not Writing About Yet
&lt;/h2&gt;

&lt;p&gt;AI agents are autonomous systems that can perform tasks on behalf of users - not just answer questions, but take actions. A browser agent, for example, can visit your website, analyze visual renderings (like screenshots), inspect the DOM structure, and interpret the accessibility tree, all to gather the information before reporting back to the user.&lt;/p&gt;

&lt;p&gt;Google’s agent-friendly guidelines come down to one principle: if an agent cannot see it clearly and click it confidently, it will skip it.&lt;/p&gt;

&lt;p&gt;Google links to a separate guide on &lt;a href="https://web.dev/articles/ai-agent-site-ux" rel="noopener noreferrer"&gt;agent-friendly website best practices&lt;/a&gt;. Although it is worth reading alongside this, here is a summary of the best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags for interactive elements. If you must use a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, give it &lt;code&gt;role="button"&lt;/code&gt; and a &lt;code&gt;tabindex&lt;/code&gt;. Agents are trained to recognize semantic HTML as actionable.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;cursor: pointer&lt;/code&gt; in CSS on clickable elements. This is treated as an explicit actionability signal.&lt;/li&gt;
&lt;li&gt;Keep your layout stable. Agents that take screenshots get confused by elements that shift position across pages, like a cart button that appears in a different location per product category.&lt;/li&gt;
&lt;li&gt;Always link &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; tags to their inputs using the &lt;code&gt;for&lt;/code&gt; attribute. This tells the agent what a form field is actually for.&lt;/li&gt;
&lt;li&gt;Make sure interactive elements are larger than 8 square pixels. Visual analysis filters out anything smaller, so a tiny button effectively does not exist to an agent.&lt;/li&gt;
&lt;li&gt;Remove transparent overlays and ghost elements. If a layer covers an interactive element, even a fully transparent one, the agent may discard the node underneath it entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, the &lt;a href="https://ucp.dev/latest/specification/overview/" rel="noopener noreferrer"&gt;Universal Commerce Protocol (UCP)&lt;/a&gt; is emerging as a standard that will allow Search Agents to do significantly more on commercial sites. If you run an e-commerce site or any site where a user might want to complete a transaction, this is something to track.&lt;/p&gt;

&lt;p&gt;This is not something you need to act on today. But it is the direction things are heading, and being aware of it now means you are not late to the party.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Summary: What to Actually Do
&lt;/h2&gt;

&lt;p&gt;The foundation has not changed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a site that is technically clean, fast, crawlable, and can be indexed.&lt;/li&gt;
&lt;li&gt;Create content that has a real point of view and provides something a generic summary cannot.&lt;/li&gt;
&lt;li&gt;Make your most important facts explicit through structured data.&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fdot16ykbvscy6jsx9kw3.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.amazonaws.com%2Fuploads%2Farticles%2Fdot16ykbvscy6jsx9kw3.png" alt="Practical checklist for both personal and commercial sites" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://blog.google/products-and-platforms/products/search/search-io-2026/#powerful-ai" rel="noopener noreferrer"&gt;A new era for AI Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;Optimizing your website for generative AI features on Google Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/ai-agent-site-ux" rel="noopener noreferrer"&gt;Build agent-friendly websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://schema.org/docs/full.html" rel="noopener noreferrer"&gt;Schema.org. Full type hierarchy.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@prtkgoswami8/your-portfolio-is-invisible-heres-how-i-fixed-mine-193b1f503fc7" rel="noopener noreferrer"&gt;Your Portfolio Is Invisible. Here is How I Fixed Mine.&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>google</category>
    </item>
    <item>
      <title>Part I: Don't Wait for Data. Render What You Know.</title>
      <dc:creator>Pratik Goswami</dc:creator>
      <pubDate>Tue, 19 May 2026 02:30:00 +0000</pubDate>
      <link>https://dev.to/pratik_goswami/part-i-dont-wait-for-data-render-what-you-know-4gaa</link>
      <guid>https://dev.to/pratik_goswami/part-i-dont-wait-for-data-render-what-you-know-4gaa</guid>
      <description>&lt;p&gt;&lt;em&gt;Part I of the series "Don't Let the User Wait - Render Quickly to Keep Their Attention."&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem hiding inside SSR
&lt;/h2&gt;

&lt;p&gt;Server-Side Rendering promises the world: fast first paint, SEO-friendly markup, no content flash. For simple pages, it delivers. But real product pages are rarely simple. They need authentication checks, user details and live data to name a few - a cascade of API calls that all have to resolve before the server can respond.&lt;/p&gt;

&lt;p&gt;If the page only renders after all data are fetched, the server is just the new bottleneck and the user experience is hurt.&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.amazonaws.com%2Fuploads%2Farticles%2F0edip2hf4ah4ihf91sst.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.amazonaws.com%2Fuploads%2Farticles%2F0edip2hf4ah4ihf91sst.png" alt="Problem: Page is only rendered after Auth is checked and User Details and Chats are fetched" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets consider the case of a chat based application. The page needs to check user authentication before calling for the user details and user chats. All three API calls - auth, user details, chats - must complete before a single pixel is painted. Meanwhile, the user only sees a white screen and their experience is indistinguishable from a slow client-side app, regardless of whether the work is happening on the server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"We moved to SSR and our Lighthouse score went up, but users still complain it feels slow."&lt;/em&gt; - every other engineering team.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The fix: Render the shell, let components own their data
&lt;/h2&gt;

&lt;p&gt;The key insight is that not all data is equally critical for the initial render. In the case depicted by the image above, &lt;em&gt;auth&lt;/em&gt; is critical to determine what the user is shown - you need a valid session before you send anything. But &lt;em&gt;userDetails&lt;/em&gt; and &lt;em&gt;chats&lt;/em&gt;? Those are content. The page can exist without them, and they can load in after the shell is already painted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This article explores this using React and Next.js.&lt;/strong&gt; If you're working with a different stack, the same principle applies at the BFF layer - framework-agnostic and without any dependency on Server Components. That's covered in &lt;strong&gt;Part II - coming 27 May.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;&lt;strong&gt;React Server Components&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://react.dev/reference/react/Suspense" rel="noopener noreferrer"&gt;&lt;strong&gt;Suspense&lt;/strong&gt;&lt;/a&gt; make this the natural way to build. The idea splits into two clear phases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 - Render the shell immediately.&lt;/strong&gt; &lt;br&gt;
The page render is only blocked for auth. Once the session is confirmed, the HTML shell - with navigation, layout and skeleton placeholders - is sent to the browser and rendered. The user sees something real and fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2 - Each component fetches its own data.&lt;/strong&gt; &lt;br&gt;
&lt;code&gt;UserDetails&lt;/code&gt; and &lt;code&gt;Chats&lt;/code&gt; are asynchronous Server Components wrapped in Suspense boundaries. They each  independently fetch their own data on the server and render their  resolved HTML into the page as they complete - no waiting for each  other, no coordinating through a parent.&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.amazonaws.com%2Fuploads%2Farticles%2F3yppfyfcipp7mpqd9dw7.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.amazonaws.com%2Fuploads%2Farticles%2F3yppfyfcipp7mpqd9dw7.png" alt="Solution: Shell Rendered after auth and components render as data is fetched" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user lands on a page that is already painted - navigation is visible, layout is in place, skeleton loaders hold the space where content will appear. Then &lt;code&gt;UserDetails&lt;/code&gt; and &lt;code&gt;Chats&lt;/code&gt; swap in, each one independently, as soon as its data is ready. &lt;/p&gt;


&lt;h2&gt;
  
  
  Suspense as the rendering contract
&lt;/h2&gt;

&lt;p&gt;React Suspense is the mechanism that wires this process together. Each data-dependent section of the page is wrapped in a &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;  boundary with a skeleton fallback. React renders the skeleton  immediately and replaces it with the resolved content the moment the  component is ready to render.&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;// app/home/page.tsx (Next.js App Router)&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;Suspense&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;react&lt;/span&gt;&lt;span class="dl"&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;UserDetailsSkeleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ChatsSkeleton&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;./skeletons&lt;/span&gt;&lt;span class="dl"&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Only auth blocks here — this is the only awaited call&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;getSession&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;session&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;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Renders immediately — no data dependency */&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;Header&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Skeleton shown instantly, swapped when data arrives */&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;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&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;UserDetailsSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserDetails&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Resolves independently — doesn't wait for UserDetails */&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;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&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;ChatsSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChatList&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Each Suspense boundary is independently resolvable. &lt;code&gt;UserDetails&lt;/code&gt; can swap in before &lt;code&gt;ChatList&lt;/code&gt;  is ready and vice versa. The user sees a progressively enriched page  rather than a binary flip from white screen to full content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Each component owns its own fetch
&lt;/h3&gt;

&lt;p&gt;The other half of the pattern is that each async Server Component fetches only what it needs, independently. There's no parent component collecting all the data and passing it down:&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;// components/UserDetails.tsx&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;UserDetails&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;userId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;details&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;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/details`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&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="nx"&gt;r&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileCard&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// components/ChatList.tsx&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;ChatList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;userId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chats&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;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/chats`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&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="nx"&gt;r&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChatFeed&lt;/span&gt; &lt;span class="na"&gt;chats&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;chats&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What React does under the hood:&lt;/strong&gt; &lt;br&gt;
&lt;code&gt;renderToPipeableStream&lt;/code&gt;  sends the HTML shell to the browser first. As each Suspense boundary  resolves, React emits an inline script chunk over the same open  connection. The browser swaps the skeleton for the real content without a  full page reload or a separate network request.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Don't forget the error boundary
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;Suspense&lt;/code&gt; handles the loading state, pair it with an &lt;code&gt;ErrorBoundary&lt;/code&gt; to handle failure. This ensures that a broken API call degrades at the component level rather than crashing the whole page:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;fallback&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;ChatsErrorCard&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&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;ChatsSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChatList&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&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;Now if &lt;code&gt;ChatList&lt;/code&gt; fails - network timeout, downstream 500,  or anything else - the rest of the page is completely unaffected.  Failed components can be retried either manually or automatically using  exponential backoff strategy. Now the page has a significant improvement over the old SSR  model where a single API failure would crash the entire render.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this does to your metrics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Classic SSR — wait for all&lt;/th&gt;
&lt;th&gt;Server Components + Suspense&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTFB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auth + all API calls combined&lt;/td&gt;
&lt;td&gt;Auth only — often 80–90% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;After all data resolves&lt;/td&gt;
&lt;td&gt;Shell arrives immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tied to the slowest API call&lt;/td&gt;
&lt;td&gt;Each section loads as ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One failure crashes the page&lt;/td&gt;
&lt;td&gt;Per-section Error Boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data ownership&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Centralised in page component&lt;/td&gt;
&lt;td&gt;Each component owns its fetch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When to apply this pattern
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Your page has a mix of critical and non-critical data.&lt;/strong&gt; The pattern shines when there's a clear hierarchy - shell first, primary content second, secondary content third.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend calls have variable latency.&lt;/strong&gt; If auth is fast but recommendations or chat APIs are slow, don't hold up the whole page. Let the slow sections render independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're on Next.js App Router.&lt;/strong&gt; Pages Router with &lt;code&gt;getServerSideProps&lt;/code&gt; doesn't support this model - all data fetching is centralised and blocking. App Router's async Server Components and &lt;code&gt;loading.tsx&lt;/code&gt; files are designed precisely for this pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The old SSR model treats the page as a single blocking unit - nothing goes to the browser until everything is ready. Server Components and Suspense treat the page as a composition of independently resolvable sections, each with its own data timeline.&lt;/p&gt;

&lt;p&gt;Block render only on auth, and render the shell immediately. Let each component own its data and render its content when ready. Pair every Suspense with an ErrorBoundary so failures stay local and can be retried.&lt;/p&gt;

&lt;p&gt;The result is a page that feels genuinely fast - not because you moved computation around, but because you changed &lt;em&gt;when the user sees something&lt;/em&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not on React or Next.js?&lt;/strong&gt; &lt;br&gt;
The same principle applies - but the fix lives in the BFF, not the frontend framework. Part II covers the framework-agnostic approach using HTTP streaming. Works with Vue, Svelte, Angular, or anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part II - Don't Wait for APIs. Send What You Have. → Coming 27 May.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your Portfolio Is Invisible. Here's How I Fixed Mine.</title>
      <dc:creator>Pratik Goswami</dc:creator>
      <pubDate>Tue, 12 May 2026 02:30:00 +0000</pubDate>
      <link>https://dev.to/pratik_goswami/your-portfolio-is-invisible-heres-how-i-fixed-mine-1fek</link>
      <guid>https://dev.to/pratik_goswami/your-portfolio-is-invisible-heres-how-i-fixed-mine-1fek</guid>
      <description>&lt;p&gt;Everyone is building. AI tools have made it trivially easy to spin up a beautiful portfolio or product site in an afternoon and deploy it to Vercel or Netlify before dinner. But here is the thing nobody talks about: &lt;strong&gt;Vercel gives you a URL. It does not give you an audience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I learned this firsthand. I have had a personal website at &lt;a href="https://www.pratikgoswami.dev/" rel="noopener noreferrer"&gt;pratikgoswami.dev&lt;/a&gt; for years. It looked great - it had my projects, experience, contact details - but the only people who ever visited it were recruiters who already had my resume or connections who clicked through from my LinkedIn. The site was essentially a &lt;strong&gt;private document with a public URL.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then, in late April 2026, I spent two days fixing that. Here is exactly what I did and what happened after.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, Understand Who You Are Actually Building For
&lt;/h2&gt;

&lt;p&gt;Most developers optimize their site for one audience: humans. But your site has two types of readers: humans and machines. And increasingly, it is the machines that decide whether the humans ever find you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SEO (Search Engine Optimization)&lt;/strong&gt; is about helping Search Engines (like Google) crawl, understand, and rank your content so people find you through search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AEO (Answer Engine Optimization)&lt;/strong&gt; is newer, and arguably more important today. It is about helping AI assistants like ChatGPT, Gemini, and Claude form a confident, accurate understanding of who you are, so they can mention you when someone asks &lt;em&gt;"who are good full-stack developers with fintech experience?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it this way: &lt;strong&gt;SEO gets you on the map. AEO makes you a landmark.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With LLMs becoming the next search interface, both matter. I decided to tackle them together.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Honest Baseline
&lt;/h2&gt;

&lt;p&gt;Before I made any changes, my Google Search Console data tells an honest story. The site had a tiny trickle of impressions starting in early April, the result of some rudimentary metadata I had set up previously. But it was flat, sparse, and averaging a position of 36+ in search results, effectively invisible.&lt;/p&gt;

&lt;p&gt;I deployed my SEO and AEO changes on &lt;strong&gt;April 28th, 2026&lt;/strong&gt;. By &lt;strong&gt;April 30th&lt;/strong&gt;, impressions spiked sharply. In the 12 days since, the site has accumulated &lt;strong&gt;52 impressions&lt;/strong&gt;, with 2 actual clicks and a 1.8% CTR. Small numbers, but the trajectory is the story. The spike is real and it came immediately after the changes.&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.amazonaws.com%2Fuploads%2Farticles%2F1g4n0rgnrbyf7efu1t7z.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.amazonaws.com%2Fuploads%2Farticles%2F1g4n0rgnrbyf7efu1t7z.png" alt="Impression &amp;amp; Click Performance on Google Search Console" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let me walk you through exactly what I changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Traditional SEO - Making It Readable for Google
&lt;/h2&gt;

&lt;p&gt;My site is built with &lt;strong&gt;Next.js (App Router), TypeScript, and Sanity CMS&lt;/strong&gt;. It looked clean to any human visitor. To a crawler, it was a meaningless soup of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Semantic HTML Refactoring
&lt;/h3&gt;

&lt;p&gt;The simplest change with the highest impact. I replaced generic container divs with meaningful HTML elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"skills-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skills-section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Skills&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    {skillList.map(({title, skills}) =&amp;gt; (
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-container"&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"{title}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{title}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-item-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          {skills &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; skills.map((skill, index) =&amp;gt; (
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-item"&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"{`${title}-${index}`}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{skill}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          ))}
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    ))}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"skills-section"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Skills"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skills-section-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Technical Skills&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skills-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {skillList.map(({ title, skills }) =&amp;gt; (
      &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-container"&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"{title}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{title}&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-item-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          {skills &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; skills.map((skill, index) =&amp;gt; (
            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"skill-item"&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"{`${title}-${index}`}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{skill}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
          ))}
        &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
    ))}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does this matter? A crawler reads your HTML like an outline. &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; through &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; tags are chapter headings. &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; are meaningful containers. Without them, Googlebot sees a flat document with no hierarchy and has to guess what is important.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fixing Content Discoverability
&lt;/h3&gt;

&lt;p&gt;This one surprised me. I had project details that rendered conditionally, after a user interaction: a click to expand, a tab switch, a state toggle. It looked fine in the browser, but Googlebot crawls the &lt;strong&gt;initial DOM&lt;/strong&gt;, not the post-interaction state. If your content only renders after a click, it effectively does not exist to Google.&lt;/p&gt;

&lt;p&gt;I refactored these components to render all content in the initial HTML, using CSS to control visibility rather than React state.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Adding Guides for Crawlers
&lt;/h3&gt;

&lt;p&gt;I added &lt;code&gt;robots.ts&lt;/code&gt; and &lt;code&gt;sitemap.ts&lt;/code&gt; to my project - two small files that do an important job. &lt;code&gt;robots.ts&lt;/code&gt; tells crawlers which pages they are allowed to visit. &lt;code&gt;sitemap.ts&lt;/code&gt; hands them a complete map of every page on your site so nothing gets missed. Next.js App Router makes this clean.&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;// app/robots.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;robots&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Robots&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="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;disallow&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="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Routes that you dont want Crawler to see&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.pratikgoswami.dev/sitemap.xml&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/sitemap.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&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="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="s2"&gt;https://www.pratikgoswami.dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;monthly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;The last step is submitting your sitemap in Google Search Console. That single action transforms your site from something Google might eventually find into something Google knows exists today.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Performance and Core Web Vitals
&lt;/h3&gt;

&lt;p&gt;Google uses Core Web Vitals, specifically &lt;strong&gt;LCP (Largest Contentful Paint)&lt;/strong&gt;, as a ranking signal. LCP measures how long it takes for the largest visible element on your page to load - the lower the number, the faster your site feels to a visitor. &lt;/p&gt;

&lt;p&gt;I had been importing FontAwesome icons as an external dependency, which added unnecessary weight to the bundle. I replaced the animated canvas background with a native Canvas API implementation, cutting the external dependency entirely and improving load performance.&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.amazonaws.com%2Fuploads%2Farticles%2F4glw5i3hkfj5iiv5nmwt.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.amazonaws.com%2Fuploads%2Farticles%2F4glw5i3hkfj5iiv5nmwt.png" alt="Core Web Vitals" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: AEO - Making It Readable for AI
&lt;/h2&gt;

&lt;p&gt;Most developers fix their SEO and think the job is done. It is not. Search behavior has fundamentally shifted. People are using ChatGPT, Gemini, and Claude as search engines now, asking them to recommend developers, tools, and products. If an AI model does not know who you are, you are invisible to an entirely new class of search. AEO is how you fix that.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. JSON-LD Structured Data
&lt;/h3&gt;

&lt;p&gt;Think of JSON-LD as your verified ID card for the internet - it tells search engines which queries you are relevant to, and gives AI models the structured facts they need to represent you accurately.&lt;/p&gt;

&lt;p&gt;I added a &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; block to my site's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; using the &lt;strong&gt;Person&lt;/strong&gt; and &lt;strong&gt;SoftwareApplication&lt;/strong&gt; schemas from Schema.org. This gives AI models explicit, machine-readable facts rather than forcing them to infer who you are from your prose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Script&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"schema-person"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;
    &lt;span class="na"&gt;dangerouslySetInnerHTML=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;
      &lt;span class="na"&gt;__html:&lt;/span&gt; &lt;span class="na"&gt;JSON.stringify&lt;/span&gt;&lt;span class="err"&gt;({&lt;/span&gt;
          &lt;span class="err"&gt;"@&lt;/span&gt;&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;https:&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="na"&gt;schema.org&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="err"&gt;"@&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Person&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Pratik&lt;/span&gt; &lt;span class="na"&gt;Goswami&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="na"&gt;jobTitle:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Frontend&lt;/span&gt; &lt;span class="na"&gt;Software&lt;/span&gt; &lt;span class="na"&gt;Engineer&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="na"&gt;url:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;https:&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="na"&gt;www.pratikgoswami.dev&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="na"&gt;sameAs:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
          &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;https:&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="na"&gt;www.linkedin.com&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;prtkgoswami&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;https:&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="na"&gt;github.com&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;prtkgoswami&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="err"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;worksFor:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
          &lt;span class="err"&gt;"@&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Organization&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;TikTok&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;IBM&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
          &lt;span class="err"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;description:&lt;/span&gt;
          &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Frontend&lt;/span&gt; &lt;span class="na"&gt;Engineer&lt;/span&gt; &lt;span class="na"&gt;specializing&lt;/span&gt; &lt;span class="na"&gt;in&lt;/span&gt; &lt;span class="na"&gt;React&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;TypeScript&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;scalable&lt;/span&gt; &lt;span class="na"&gt;UI&lt;/span&gt; &lt;span class="na"&gt;systems.&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
      &lt;span class="err"&gt;}),&lt;/span&gt;
    &lt;span class="err"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, an LLM has to guess who you are based on scattered text. With it, you are handing the model a verified fact sheet.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Triangle of Trust - Entity Linking via sameAs
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;sameAs&lt;/code&gt; property is subtle but powerful. By linking your personal domain to your LinkedIn and GitHub profiles, you are creating a &lt;strong&gt;web of verification&lt;/strong&gt; across multiple authoritative sources.&lt;/p&gt;

&lt;p&gt;LLMs build a confidence score around named entities. If "Pratik Goswami" appears on a personal website, a LinkedIn profile, and a GitHub account, and those sources all link to each other, the model develops a high-confidence entity. It knows who you are. Without these links, you are just an unnamed node in a vast graph.&lt;/p&gt;

&lt;p&gt;This is the triangle of trust: &lt;strong&gt;your domain, LinkedIn, and GitHub&lt;/strong&gt;. Each point of the triangle reinforces the others.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Writing for RAG - Declarative, Specific Descriptions
&lt;/h3&gt;

&lt;p&gt;Retrieval-Augmented Generation (RAG) is how many LLMs fetch context before answering. They pull relevant chunks of text from indexed sources. That means &lt;strong&gt;how you write about your projects matters&lt;/strong&gt; as much as the schema you add.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vague (bad for RAG):&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Passionate developer who loves building impactful things."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Specific (good for RAG):&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Built Gears Connect, a B2B automotive parts marketplace using Next.js, PostgreSQL, and Stripe, enabling real-time inventory management for 50+ vendors."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second version is indexable. It is full of specific, searchable facts. An LLM retrieving context for a query like &lt;em&gt;"full-stack developers who have built marketplace apps"&lt;/em&gt; can actually use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. JSON-LD Over llms.txt
&lt;/h3&gt;

&lt;p&gt;You may have seen chatter about &lt;code&gt;llms.txt&lt;/code&gt;, a proposed standard for helping LLMs read your site, but it is a shortcut, not a foundation. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is a plain-text file placed at the root of your domain that gives AI crawlers a human-readable summary of your site, similar to how &lt;code&gt;robots.txt&lt;/code&gt; works for search crawlers. Low effort to set up, but also low density. It carries no schema, no entity relationships, and zero influence over search rankings.&lt;/p&gt;

&lt;p&gt;JSON-LD does all of that and more. It is a W3C standard understood by Google, Bing, LinkedIn, Perplexity, and SearchGPT simultaneously. It defines explicit relationships - that "Pratik Goswami" is a &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;worksFor&lt;/code&gt; specific organizations, and is the &lt;code&gt;author&lt;/code&gt; of specific projects. It also unlocks Google Rich Results, putting your job title and links directly in the search listing. &lt;code&gt;llms.txt&lt;/code&gt; cannot do any of this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Monitoring - Closing the Loop
&lt;/h2&gt;

&lt;p&gt;Shipping SEO and AEO changes without monitoring is like sending a message and never checking if it was delivered. Monitoring closes the feedback loop - it tells you what Google has indexed, which queries are surfacing your name, and which parts of your site are actually holding a visitor's attention. Without it, you are optimizing blind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Search Console&lt;/strong&gt; was the first thing I set up, before deploying any changes. Verifying your domain and submitting your sitemap establishes a direct line of communication with Google.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The LLM test&lt;/strong&gt; is simple and satisfying: ask ChatGPT, Gemini, and Claude "Who is Pratik Goswami?" My site now surfaces accurate, confident responses across all three. That is the AEO working.&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.amazonaws.com%2Fuploads%2Farticles%2Fif2r9sd7u3feweytx8cs.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.amazonaws.com%2Fuploads%2Farticles%2Fif2r9sd7u3feweytx8cs.png" alt="Test on Gemini &amp;amp; GPT" width="800" height="341"&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.amazonaws.com%2Fuploads%2Farticles%2Fu68adlqvxf1q9gjdhry3.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.amazonaws.com%2Fuploads%2Farticles%2Fu68adlqvxf1q9gjdhry3.png" alt="Google Search Test" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Google Analytics tracking&lt;/strong&gt; using &lt;code&gt;IntersectionObserver&lt;/code&gt; lets me track which sections of my site recruiters actually read, not just that they visited. Knowing that the "&lt;a href="https://jobtrack.pratikgoswami.dev" rel="noopener noreferrer"&gt;Job Application Tracker&lt;/a&gt;" project card gets more dwell time than others is actionable data, not a vanity metric.&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&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;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&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;sectionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// User entered a section&lt;/span&gt;
                &lt;span class="nx"&gt;activeSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sectionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;enterTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;sendSectionView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sectionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;sectionId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;enterTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// User left the section → calculate time spent&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;enterTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;sendTimeSpent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sectionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;activeSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;enterTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Trigger when 50% of a section is visible&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;SECTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&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="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;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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;
  
  
  Was It Worth It?
&lt;/h2&gt;

&lt;p&gt;In two days of work, the site went from effectively invisible to ranking on the first page of Google for my name, accumulating 52 impressions in 12 days, and being recognized by ChatGPT, Gemini, and Claude when asked who I am.&lt;/p&gt;

&lt;p&gt;The numbers are early, but the direction is clear. The spike in the Google Search Console (GSC) chart is immediate and unambiguous.&lt;/p&gt;

&lt;p&gt;Here is what I want you to take away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A beautiful site and a discoverable site are not the same thing&lt;/strong&gt;. Most portfolios are the former, not the latter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AEO is not the future. It is right now&lt;/strong&gt;. LLMs are already answering career questions. If you are not structured for them, you are invisible to them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The implementation is smaller than you think&lt;/strong&gt;. Two files, one schema block, and a few hours of refactoring later, my site went from invisible to indexed. The effort was minimal. The compounding visibility was not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat your portfolio like a product&lt;/strong&gt;. Ship, measure, iterate.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Your Quick-Start Checklist
&lt;/h2&gt;

&lt;p&gt;Pick one item from this list and do it this weekend:&lt;/p&gt;

&lt;p&gt;[ ] Replace &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; wrappers with &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, and proper &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; hierarchy&lt;br&gt;
[ ] Audit for content hidden behind React state and render it in the initial HTML&lt;br&gt;
[ ] Add &lt;code&gt;robots.ts&lt;/code&gt; and &lt;code&gt;sitemap.ts&lt;/code&gt; in Next.js App Router&lt;br&gt;
[ ] Submit your sitemap in Google Search Console&lt;br&gt;
[ ] Run PageSpeed Insights and check your LCP score&lt;br&gt;
[ ] Add a &lt;code&gt;Person&lt;/code&gt; JSON-LD schema to your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;&lt;br&gt;
[ ] Add &lt;code&gt;sameAs&lt;/code&gt; links to LinkedIn and GitHub in your schema&lt;br&gt;
[ ] Rewrite your project descriptions to be specific, declarative, and technical&lt;br&gt;
[ ] Ask ChatGPT or Gemini who you are and see what they say today&lt;/p&gt;




&lt;h2&gt;
  
  
  So… What Is Next?
&lt;/h2&gt;

&lt;p&gt;This is just the starting point. The real value of setting up monitoring is that the data now tells me where to focus next - which projects are getting attention, which queries are surfacing my name, and where there is still room to improve. I will keep the structured data updated as my experience grows, revisit the content as AEO standards evolve, and let the analytics guide the next round of changes. The foundation is built. Now it gets to compound.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
