<?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: Studio1</title>
    <description>The latest articles on DEV Community by Studio1 (studio1hq).</description>
    <link>https://dev.to/studio1hq</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%2Forganization%2Fprofile_image%2F9405%2F8a799951-e4ab-4bf6-98fb-012dbe40a626.png</url>
      <title>DEV Community: Studio1</title>
      <link>https://dev.to/studio1hq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/studio1hq"/>
    <language>en</language>
    <item>
      <title>Muse Wonder Review: Benchmarks, Pricing, and Access</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Mon, 19 Oct 2026 15:35:58 +0000</pubDate>
      <link>https://dev.to/studio1hq/muse-wonder-review-benchmarks-pricing-and-access-2dd6</link>
      <guid>https://dev.to/studio1hq/muse-wonder-review-benchmarks-pricing-and-access-2dd6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Meta just released Muse Wonder and Muse Wonder Full, its first top-tier AI models. This post covers what they score, what they cost, who can use each one, and where Claude Opus 5.5 and GPT-6 Astra still come out ahead.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the independent Artificial Analysis index, Muse Wonder Full scores 56, and Muse Wonder scores 52. Claude Opus 5.5 scores 58, and GPT-6 Astra scores 53.&lt;/li&gt;
&lt;li&gt;Muse Wonder costs $4 per million input tokens and $20 per million output tokens on the API. Muse Wonder Full is not on the API at all. You only get it inside Muse Code plans that cost $50 or $200 a month.&lt;/li&gt;
&lt;li&gt;Meta's own table shows the models leading on long coding projects, legal tasks, and memory. They trail on terminal work and from-scratch programming.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meta spent most of 2026 shipping small, cheap models. On October 12, it changed lanes. &lt;a href="https://research.meta.ai/blog/muse-wonder" rel="noopener noreferrer"&gt;Muse Wonder and Muse Wonder Full&lt;/a&gt; are bigger, pricier, and aimed straight at OpenAI and Anthropic.&lt;/p&gt;

&lt;p&gt;Meta's scoreboard says they win a lot. The independent scoreboard says they get close. Both can be true, and the gap between the two is the part worth reading. Both models are also only a day old, so very few people outside Meta have used them for real work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Muse Wonder?
&lt;/h2&gt;

&lt;p&gt;Muse Wonder is a pair of AI models from Meta Superintelligence Labs, released on October 12, 2026. They are built to handle long, multi-step jobs for you, like fixing a big codebase, drafting a legal document, or planning something across several apps. Meta calls them its &lt;a href="https://about.fb.com/news/2026/10/introducing-muse-wonder-models/" rel="noopener noreferrer"&gt;most capable models so far&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is what Meta says is new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active Memory.&lt;/strong&gt; The model carries what matters about you from one chat to the next and brings it back when it is relevant to the task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Focus.&lt;/strong&gt; When a job gets hard, the model tries a different approach, recovers from mistakes, asks you a question if it is stuck, and checks in before doing anything important.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide input, text output.&lt;/strong&gt; According to the &lt;a href="https://research.meta.ai/static/muse-wonder-model-card" rel="noopener noreferrer"&gt;model card&lt;/a&gt;, it accepts text, images, video, and audio, and replies in text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big memory per request.&lt;/strong&gt; It can read up to 1 million tokens at once. That is roughly 750,000 words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closed weights.&lt;/strong&gt; You cannot download either model, &lt;a href="https://venturebeat.com/technology/metas-new-muse-wonder-and-muse-wonder-full-models-achieve-frontier-performance-but-only-the-weaker-one-is-available-via-api" rel="noopener noreferrer"&gt;VentureBeat reports&lt;/a&gt;. Meta has not said whether it will open any Wonder model, &lt;a href="https://www.axios.com/2026/10/12/meta-new-ai-models-anthropic-openai" rel="noopener noreferrer"&gt;per Axios&lt;/a&gt;. Its smaller Muse Glimmer model is open.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The models were trained at Meta's Prometheus data center in Ohio, &lt;a href="https://techcrunch.com/2026/10/12/meta-releases-its-first-frontier-model-muse-wonder/" rel="noopener noreferrer"&gt;according to TechCrunch&lt;/a&gt;. Axios adds that they went by the code name Watermelon before launch.&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%2Fegpv421yi8i2swc1anca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fegpv421yi8i2swc1anca.png" alt="Image1" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the difference between Muse Wonder and Muse Wonder Full?
&lt;/h2&gt;

&lt;p&gt;Muse Wonder is the one you can build with today. Muse Wonder Full is the stronger one, but Meta only sells it through its coding app, Muse Code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Muse Wonder&lt;/th&gt;
&lt;th&gt;Muse Wonder Full&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where you can use it&lt;/td&gt;
&lt;td&gt;Meta Model API and Muse Code&lt;/td&gt;
&lt;td&gt;Muse Code only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;$4 input and $20 output per 1M tokens&lt;/td&gt;
&lt;td&gt;Included in $50 or $200 monthly plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pay-as-you-go API&lt;/td&gt;
&lt;td&gt;Yes (model ID: muse-wonder-1.0)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context size&lt;/td&gt;
&lt;td&gt;1M tokens&lt;/td&gt;
&lt;td&gt;1M tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Artificial Analysis score&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used to train Meta's models by default&lt;/td&gt;
&lt;td&gt;No on the API, yes in Muse Code&lt;/td&gt;
&lt;td&gt;Yes, until you switch it off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Meta also says Wonder will soon power more of the Muse chat app, rolling out &lt;a href="https://about.fb.com/news/2026/10/introducing-muse-wonder-models/" rel="noopener noreferrer"&gt;first to paying subscribers&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Muse Wonder score on benchmarks?
&lt;/h2&gt;

&lt;p&gt;A benchmark is a standard test. Every model takes the same exam, and a higher score is better. Because Meta wrote the launch table itself, we will start with the independent numbers and then look at Meta's.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do outside testers say?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://artificialanalysis.ai/models/muse-wonder-full" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt; ran both models through its Intelligence Index, a mix of ten tests covering reasoning, knowledge, coding, and office-style tasks. The scores below are from &lt;a href="https://venturebeat.com/technology/metas-new-muse-wonder-and-muse-wonder-full-models-achieve-frontier-performance-but-only-the-weaker-one-is-available-via-api" rel="noopener noreferrer"&gt;VentureBeat's report&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Artificial Analysis score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5.5&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Muse Wonder Full&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 4 Argon&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Fable 5.1&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Muse Wonder&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So Wonder Full lands in a tie for second. Plain Wonder, the one on the API, sits one point under GPT-6 Astra and six points under Opus 5.5.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does Meta's own table say?
&lt;/h3&gt;

&lt;p&gt;Meta compared both models with Claude Fable 5.1, GPT-6 Astra, and Claude Opus 5.5, all set to their highest effort. These are the rows that matter most, taken from Meta's &lt;a href="https://dev.meta.ai/models/muse-wonder" rel="noopener noreferrer"&gt;model card and launch page&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%2F42btsmglix7ps0uka071.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F42btsmglix7ps0uka071.png" alt="Image2" width="800" height="1207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By our count of Meta's full 14-row table, a Muse model holds the top score on 7 rows, Opus 5.5 on 6, and GPT-6 Astra on 1. Some of those wins are razor thin. JobBench is a 0.1-point loss for Wonder Full, and its Vals Finance Agent lead over Fable 5.1 is 0.2 points.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much should you trust the table?
&lt;/h3&gt;

&lt;p&gt;Three things to know before you quote it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Meta picked the tests.&lt;/strong&gt; Its model card says some competitor results were &lt;a href="https://research.meta.ai/static/muse-wonder-model-card" rel="noopener noreferrer"&gt;run by Meta itself on a best-effort basis&lt;/a&gt;, so the settings may not suit the other models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some tests cannot be rerun.&lt;/strong&gt; Three of the scores come from Meta's internal tests (the Personal Superintelligence Index, MemoryBench, and FocusBench), according to &lt;a href="https://maccus.fr/en/meta-muse-wonder/" rel="noopener noreferrer"&gt;Maccus&lt;/a&gt;. Nobody outside Meta can check them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not every row uses the same setup.&lt;/strong&gt; On SWE-Marathon, the card says GPT-6 Astra and Fable 5.1 ran through their own coding tools, so it is not a fair side-by-side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Meta also has history here. For Muse Spark 1.1, it reported 80 on Terminal-Bench 2.1, and an &lt;a href="https://codersera.com/blog/muse-spark-1-2-benchmarks-vs-claude-opus-5-2026/" rel="noopener noreferrer"&gt;independent check came back at 76.2&lt;/a&gt;. Scores in a launch post tend to be the best case.&lt;/p&gt;

&lt;p&gt;Even the strongest legal score needs context. Harvey LAB-AA counts a task as passed only when the result meets every requirement and has no made-up facts. A score of 13.1 means roughly one task in eight clears that bar. It is the best in the table, and it is still low.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much does Muse Wonder cost?
&lt;/h2&gt;

&lt;p&gt;Muse Wonder costs $4 per million input tokens and $20 per million output tokens on the Meta Model API. Cached input, which means text you send again and again, costs $0.20 per million. Those are the rates on &lt;a href="https://dev.meta.ai/models/muse-wonder" rel="noopener noreferrer"&gt;Meta's own page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A token is a small chunk of text, a bit shorter than a word. Input is what you send in. Output is what the model writes back.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input per 1M tokens&lt;/th&gt;
&lt;th&gt;Output per 1M tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Muse Spark 1.3&lt;/td&gt;
&lt;td&gt;$1.25&lt;/td&gt;
&lt;td&gt;$4.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 4 Argon (launch price)&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Muse Wonder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$20&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5.5&lt;/td&gt;
&lt;td&gt;$4&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Fable 5.1&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prices are from &lt;a href="https://venturebeat.com/technology/metas-new-muse-wonder-and-muse-wonder-full-models-achieve-frontier-performance-but-only-the-weaker-one-is-available-via-api" rel="noopener noreferrer"&gt;VentureBeat's comparison&lt;/a&gt;. Argon's price goes to $4 and $20 after its launch period. Our &lt;a href="https://www.studio1hq.com/blog/gemini-4-argon-benchmarks-pricing-access" rel="noopener noreferrer"&gt;Gemini 4 Argon review&lt;/a&gt; covers that in detail.&lt;/p&gt;

&lt;p&gt;Two extra costs to know about. Web search through the API is $2.50 per 1,000 searches. And Meta caps each team at 3,000 requests and 4 million tokens a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it cost per task?
&lt;/h3&gt;

&lt;p&gt;The price per token does not tell you the bill. What matters is how many tokens a model uses to finish a job. Artificial Analysis measured the cost of its average test task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Muse Wonder: $4.52&lt;/li&gt;
&lt;li&gt;Claude Opus 5.5: $5.98&lt;/li&gt;
&lt;li&gt;GPT-6 Astra: $3.26&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wonder is cheaper than Opus 5.5 here because it writes less. &lt;a href="https://www.beri.net/article/meta-muse-wonder-full-pricing-benchmarks-vs-opus-5-5-gpt-6-astra-api-only-standard-muse-code-contributor-mode-training" rel="noopener noreferrer"&gt;One report on Artificial Analysis data&lt;/a&gt; puts it at about 130 million output tokens for Wonder against 260 million for Opus 5.5. The same report says Wonder takes 19.27 seconds to start answering, compared with a 5.52 second median. That makes it a poor fit for anything where a person is waiting on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you get access to Muse Wonder?
&lt;/h2&gt;

&lt;p&gt;There are three ways in right now.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Meta Model API.&lt;/strong&gt; Sign up at &lt;a href="https://dev.meta.ai/models/muse-wonder" rel="noopener noreferrer"&gt;dev.meta.ai&lt;/a&gt; and call muse-wonder-1.0. Meta says you can point an existing OpenAI-style client at it, so testing it in an app you already have should be quick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Muse Code.&lt;/strong&gt; This is Meta's coding assistant for the terminal. The Power Usage plan is $50 a month and gives 20 times the usage of the Everyday plan. The Maximum Usage plan is $200 a month and gives 80 times. Both include Muse Wonder Full.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Muse app.&lt;/strong&gt; Meta says Wonder is coming to it soon, starting with paid subscribers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no free tier for Wonder Full, and there are no downloadable weights for either model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does Meta train on your data?
&lt;/h2&gt;

&lt;p&gt;It depends on where you use the model. This is the part of the launch that is easiest to miss.&lt;/p&gt;

&lt;p&gt;On the API, Muse Wonder runs in Standard mode, where Meta says your content is not used for training. Muse Code subscriptions default to Contributor mode instead. In that mode, Meta can use your prompts, code, files, and the replies to train future models. You can switch it off, but only for what you send afterward. The details are in &lt;a href="https://venturebeat.com/technology/metas-new-muse-wonder-and-muse-wonder-full-models-achieve-frontier-performance-but-only-the-weaker-one-is-available-via-api" rel="noopener noreferrer"&gt;VentureBeat's report&lt;/a&gt; and Meta's own subscription terms.&lt;/p&gt;

&lt;p&gt;If you work with client code, check that setting before you paste in a single file. Companies with strict rules can also ask Meta about zero data retention. It comes with trade-offs, like no file uploads and no built-in web search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does Muse Wonder still fall short?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal work.&lt;/strong&gt; On Terminal-Bench 4.0-AA, Wonder Full scores 57.1 against 59.6 for Opus 5.5 and 59.1 for GPT-6 Astra. Plain Wonder scores 52.0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building from scratch.&lt;/strong&gt; On ProgramBench, Opus 5.5 scores 70.5. Wonder Full scores 59.0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health questions.&lt;/strong&gt; Opus 5.5 and GPT-6 Astra both beat Wonder Full on HealthBench Pro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facts from memory.&lt;/strong&gt; On Artificial Analysis's AA-Omniscience test, &lt;a href="https://ijr.com/discover/tech-2026-10-12-meta-releases-muse-wonder-claims-ai-benchmark-gains-d73b5e62" rel="noopener noreferrer"&gt;reported by IJR&lt;/a&gt;, Wonder Full scores 30 and Wonder scores 25. Opus 5.5 scores 46, GPT-6 Astra 43, and Gemini 4 Argon 42.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed to first word.&lt;/strong&gt; Covered above. It is slow to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Known weak spots.&lt;/strong&gt; Meta's model card says 3D and spatial reasoning is still a limit. It also says the models can slip on multi-step jobs in unfamiliar situations, and that they are not meant for anyone under 18.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety report.&lt;/strong&gt; Meta says a full safety report is coming "soon." The model card says that without extra safeguards, Meta cannot rule out the model reaching its high-risk level for chemical and biological topics. So safeguards are switched on, and they may refuse some legitimate science requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should you check before you switch?
&lt;/h2&gt;

&lt;p&gt;Do not switch on a headline number. Here is a short checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test on your own work.&lt;/strong&gt; Pull 20 real tasks from your own repo, docs, or inbox. Run them through your current model and through Muse Wonder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log the cost per task.&lt;/strong&gt; The token price is the same as Opus 5.5, so the saving only shows up if Wonder really writes less on your work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time the first answer.&lt;/strong&gt; If a person waits on the result, 19 seconds is a long wait.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick the right door.&lt;/strong&gt; Use the API for work code. Only use Muse Code with Contributor mode off, or for code you do not mind sharing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait if you are regulated.&lt;/strong&gt; If you work in health, law, or finance, hold off for the full safety report.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which should you pick: Muse Wonder, Claude Opus 5.5, or GPT-6 Astra?
&lt;/h2&gt;

&lt;p&gt;There is no single winner. The independent scores point to this split:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pick Muse Wonder&lt;/strong&gt; if you run very long coding jobs or legal-style drafts, and you want Opus-level token prices with fewer tokens used. Our &lt;a href="https://www.studio1hq.com/blog/claude-opus-5-5-review-motion-videos" rel="noopener noreferrer"&gt;Claude Opus 5.5 review&lt;/a&gt; has the other side of that comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick Claude Opus 5.5&lt;/strong&gt; if you want the highest overall score, strong from-scratch programming, and faster first answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick GPT-6 Astra&lt;/strong&gt; if cost per task and terminal work matter most, and you can live with a higher token price.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Muse Wonder?
&lt;/h3&gt;

&lt;p&gt;Muse Wonder is Meta's new AI model for long, multi-step work such as coding, legal drafting, and personal planning. It launched on October 12, 2026, alongside a stronger version called Muse Wonder Full.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Muse Wonder free?
&lt;/h3&gt;

&lt;p&gt;No. The API costs $4 per million input tokens and $20 per million output tokens. Muse Wonder Full needs a Muse Code plan at $50 or $200 a month. Meta says the Muse app will get Wonder first for paying subscribers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Muse Wonder open source?
&lt;/h3&gt;

&lt;p&gt;No. Neither Wonder model has downloadable weights. Meta has not said whether it will open any of them. Its smaller Muse Glimmer model is open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Muse Wonder better than Claude Opus 5.5?
&lt;/h3&gt;

&lt;p&gt;Not overall. Artificial Analysis scores Opus 5.5 at 58, Muse Wonder Full at 56, and Muse Wonder at 52. Meta's table shows Wonder ahead on long coding and legal tests, and Opus 5.5 ahead on terminal work and building programs from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Muse Wonder Full through the API?
&lt;/h3&gt;

&lt;p&gt;No. Muse Wonder Full is only available inside Muse Code on the $50 and $200 plans. The API serves plain Muse Wonder.&lt;/p&gt;

&lt;h3&gt;
  
  
  How big is the context window?
&lt;/h3&gt;

&lt;p&gt;Both models can read up to 1 million tokens in one request, which is roughly 750,000 words.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can Muse Wonder handle images, audio, and video?
&lt;/h3&gt;

&lt;p&gt;Yes for input. It accepts text, images, audio, and video. It only replies in text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Meta use my data to train Muse Wonder?
&lt;/h3&gt;

&lt;p&gt;Not on the API, where Standard mode applies. Muse Code defaults to Contributor mode, which lets Meta train on what you submit. You can switch it off for future messages.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>DevRel Agency vs In-House DevRel Hire: Costs, Timelines, and When Each One Makes Sense</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Sat, 17 Oct 2026 16:55:54 +0000</pubDate>
      <link>https://dev.to/studio1hq/devrel-agency-vs-in-house-devrel-hire-costs-timelines-and-when-each-one-makes-sense-3112</link>
      <guid>https://dev.to/studio1hq/devrel-agency-vs-in-house-devrel-hire-costs-timelines-and-when-each-one-makes-sense-3112</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An illustrative US hiring scenario comes to about $195,000 annually, assuming a $136,300 base salary and benefits equal to 30% of total compensation. This is not a verified DevRel salary average.&lt;/li&gt;
&lt;li&gt;SaaSHero estimates DevRel agency retainers at $8,000–$28,000 per month. Actual quotes depend on scope, staffing, and contract terms.&lt;/li&gt;
&lt;li&gt;An in-house hire suits ongoing ownership and close product collaboration. An agency can provide specialist expertise and additional execution capacity.&lt;/li&gt;
&lt;li&gt;A hybrid setup combines an internal owner with external support for agreed projects or ongoing work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;Stack Overflow's 2025 survey&lt;/a&gt; found that 84% of developers use or plan to use AI tools, up from 76% the year before. The same survey found that 46% of developers do not trust what those tools tell them, up from 31%. These findings show a gap between AI adoption and trust in its accuracy. They do not establish how often developers check original sources.&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%2Fcuzrzvv7qy1qftlc4q8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcuzrzvv7qy1qftlc4q8m.png" alt="Image-1" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That changes what you need from developer relations. Someone has to make your docs, tutorials, and community answers good enough to be quoted, and good enough to survive a double-check. The question is who that someone should be: a person on your payroll, or an agency on a retainer.&lt;/p&gt;

&lt;p&gt;This guide compares the cost and timeline of each option, and tells you when to pick which.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do the numbers look like side by side?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;In-house hire&lt;/th&gt;
&lt;th&gt;DevRel agency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Annual cost&lt;/td&gt;
&lt;td&gt;Illustrative US scenario: about $195,000 in salary and benefits&lt;/td&gt;
&lt;td&gt;$96,000 to $180,000 at &lt;a href="https://draft.dev/learn/top-technical-content-marketing-agencies" rel="noopener noreferrer"&gt;$8,000&lt;/a&gt; to &lt;a href="https://hackmamba.io/pricing" rel="noopener noreferrer"&gt;$15,000&lt;/a&gt; a month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Additional costs&lt;/td&gt;
&lt;td&gt;Recruitment, travel, events, tools and program expenses&lt;/td&gt;
&lt;td&gt;Onboarding, travel, tools and out-of-scope work, depending on the contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to start&lt;/td&gt;
&lt;td&gt;Allow for recruitment, notice period and onboarding&lt;/td&gt;
&lt;td&gt;Depends on team availability and onboarding requirements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commitment&lt;/td&gt;
&lt;td&gt;Ongoing employment&lt;/td&gt;
&lt;td&gt;Monthly, project-based or longer-term agreements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Dedicated ownership and close product collaboration&lt;/td&gt;
&lt;td&gt;Specialist support, defined projects and additional capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These figures are budgeting references, not equivalent service packages. Compare the responsibilities, hours, and deliverables included in each option.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much does an in-house DevRel hire cost?
&lt;/h2&gt;

&lt;p&gt;Salary data for DevRel is messy. Titles overlap, and some sources report base pay while others report total pay. Here is what the main ones say.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Reported pay&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.payscale.com/research/US/Job=Developer_Relations_Manager/Salary" rel="noopener noreferrer"&gt;Payscale&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DevRel manager&lt;/td&gt;
&lt;td&gt;$100,000 average base, middle half between $64,000 and $148,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.refontelearning.com/blog/developer-relations-devrel-career-guide" rel="noopener noreferrer"&gt;Refonte Learning&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Developer advocate&lt;/td&gt;
&lt;td&gt;About $136,300 average, senior about $171,300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.refontelearning.com/blog/developer-relations-devrel-career-guide" rel="noopener noreferrer"&gt;Refonte Learning&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Developer relations engineer&lt;/td&gt;
&lt;td&gt;About $143,000 average, senior about $170,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://skillscouter.com/how-to-get-a-devrel-role/" rel="noopener noreferrer"&gt;SkillScouter&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mid-level developer advocate&lt;/td&gt;
&lt;td&gt;$135,000 to $180,000 total pay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://jobrise.io/en/blog/developer-relations-careers-guide-2026/" rel="noopener noreferrer"&gt;Jobrise&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DevRel manager (US)&lt;/td&gt;
&lt;td&gt;$150,000 to $230,000 base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://jobrise.io/en/blog/developer-relations-careers-guide-2026/" rel="noopener noreferrer"&gt;Jobrise&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Head of DevRel (US)&lt;/td&gt;
&lt;td&gt;$190,000 to $300,000 or more base&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the math below, we use $136,300, the average for a developer advocate. If your market pays less, scale the numbers down.&lt;/p&gt;

&lt;h3&gt;
  
  
  The salary is not the real cost
&lt;/h3&gt;

&lt;p&gt;The US Bureau of Labor Statistics says that in June 2026, &lt;a href="https://www.bls.gov/opub/ted/2026/compensation-costs-for-private-industry-workers-averaged-46-89-per-hour-worked-in-june-2026.htm" rel="noopener noreferrer"&gt;wages were 70% of what private employers spend on a worker, and benefits were the other 30%&lt;/a&gt;. That puts benefits at roughly 43% on top of salary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mid-level advocate:&lt;/strong&gt; $136,300 in salary becomes about $195,000 a year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Senior advocate:&lt;/strong&gt; $171,300 in salary becomes about $245,000 a year.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then come the extras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recruiter fees.&lt;/strong&gt; &lt;a href="https://dev.to/d_v_/time-to-hire-for-engineers-95-days-in-2026-2dbn"&gt;15 to 25% of first-year pay is common&lt;/a&gt; for agency placements. On $136,300, that is roughly $20,000 to $34,000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A program budget.&lt;/strong&gt; Events, travel, swag, community tools, and hosting for demos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gaps.&lt;/strong&gt; One person has one set of strengths and one calendar. If they quit, the hiring clock starts over.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How much does a DevRel agency cost?
&lt;/h2&gt;

&lt;p&gt;Agency pricing is just as scattered. Here are the ranges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type of help&lt;/th&gt;
&lt;th&gt;Monthly price&lt;/th&gt;
&lt;th&gt;Typical contract&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Solo DevRel consultant&lt;/td&gt;
&lt;td&gt;$2,000 for 5 hours a month, or $3,000 for a one-time audit&lt;/td&gt;
&lt;td&gt;Monthly&lt;/td&gt;
&lt;td&gt;&lt;a href="https://yalladevrel.com/" rel="noopener noreferrer"&gt;Yalla DevRel&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One defined problem (content, docs or DevRel)&lt;/td&gt;
&lt;td&gt;From $5,000&lt;/td&gt;
&lt;td&gt;Fixed scope and deadline&lt;/td&gt;
&lt;td&gt;&lt;a href="https://hackmamba.io/pricing" rel="noopener noreferrer"&gt;Hackmamba&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical content retainer&lt;/td&gt;
&lt;td&gt;From $8,000 a month&lt;/td&gt;
&lt;td&gt;Most clients start with a 3-month pilot&lt;/td&gt;
&lt;td&gt;&lt;a href="https://draft.dev/learn/top-technical-content-marketing-agencies" rel="noopener noreferrer"&gt;Draft.dev&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-channel retainer (content, SEO or DevRel)&lt;/td&gt;
&lt;td&gt;$8,000 to $20,000&lt;/td&gt;
&lt;td&gt;3 to 6 months&lt;/td&gt;
&lt;td&gt;&lt;a href="https://marketerhire.com/blog/developer-marketing-agency" rel="noopener noreferrer"&gt;MarketerHire&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Several workstreams at once (content, docs, DevRel, paid media)&lt;/td&gt;
&lt;td&gt;From $15,000 a month&lt;/td&gt;
&lt;td&gt;Month to month, or a longer contract&lt;/td&gt;
&lt;td&gt;&lt;a href="https://hackmamba.io/pricing" rel="noopener noreferrer"&gt;Hackmamba&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-channel growth retainer&lt;/td&gt;
&lt;td&gt;$25,000 to $50,000&lt;/td&gt;
&lt;td&gt;6 to 12 months&lt;/td&gt;
&lt;td&gt;&lt;a href="https://marketerhire.com/blog/developer-marketing-agency" rel="noopener noreferrer"&gt;MarketerHire&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A fair warning. These prices come from companies that sell the service, so treat them as ballparks and get your own quotes. At Studio1, we do not publish a price list, so we are not in the table. We start with an audit of your existing developer programs, then scope the work. If you want to see what that looks like for your product, you can &lt;a href="https://cal.com/studio1/collab" rel="noopener noreferrer"&gt;book a call with us&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does the quote usually leave out?
&lt;/h3&gt;

&lt;p&gt;Two things often sit outside the retainer. &lt;a href="https://marketerhire.com/blog/devtools-marketing-agency" rel="noopener noreferrer"&gt;MarketerHire's devtools guide&lt;/a&gt; lists paid channel spend of $5,000 to $30,000 a month and conference sponsorships of $10,000 to $50,000 per event as separate costs. And most agencies start with a trial or a minimum term. &lt;a href="https://draft.dev/learn/top-technical-content-marketing-agencies" rel="noopener noreferrer"&gt;Draft.dev says most clients begin with a 3-month pilot&lt;/a&gt;, and &lt;a href="https://infrasity.com/" rel="noopener noreferrer"&gt;Infrasity asks for a 3- to 6-month minimum&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ask what is excluded before you sign. Ask whether the person you meet on the sales call is the person who does the work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where is the break-even?
&lt;/h3&gt;

&lt;p&gt;One fully loaded advocate at $195,000 a year works out to about &lt;strong&gt;$16,200 a month&lt;/strong&gt;. If an agency retainer stays under that, year one is cheaper than hiring. Above it, you are paying more than a hire for what is often a shorter, narrower commitment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do a DevRel agency and an in-house hire compare?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;In-house hire&lt;/th&gt;
&lt;th&gt;DevRel agency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;First-year cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;About $195,000 plus extras&lt;/td&gt;
&lt;td&gt;$96,000 to $180,000 at the starting prices above, plus any paid channels and events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Months&lt;/td&gt;
&lt;td&gt;Weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product knowledge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deep, and it grows&lt;/td&gt;
&lt;td&gt;Shallow at first, depends on your briefing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output volume&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One person's capacity&lt;/td&gt;
&lt;td&gt;A team's capacity, if the right people are on your account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community trust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong, people get to know a real face&lt;/td&gt;
&lt;td&gt;Weaker, since people can tell when a voice is borrowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard to scale down&lt;/td&gt;
&lt;td&gt;Easy to pause or stop at contract end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Risk if it fails&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Costly, slow exit&lt;/td&gt;
&lt;td&gt;Limited to the contract length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product feedback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Goes straight to your team&lt;/td&gt;
&lt;td&gt;Needs a deliberate process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best fit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sustained ownership, close product collaboration and ongoing community work.&lt;/td&gt;
&lt;td&gt;Additional expertise, defined projects, launches or support while building an internal team.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How long until each option starts working?
&lt;/h2&gt;

&lt;p&gt;Nobody publishes a clean time-to-hire number for DevRel roles, so we use engineering hiring data, which is the closest match. Sourced numbers are linked. The rest are our own estimates and are marked.&lt;/p&gt;

&lt;h3&gt;
  
  
  In-house timeline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write the role and get it approved:&lt;/strong&gt; 1 to 2 weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hire:&lt;/strong&gt; about 9 weeks. &lt;a href="https://proxify.io/knowledge-base/recruitment-and-hiring/why-does-hiring-a-software-engineer-take-35-days" rel="noopener noreferrer"&gt;Proxify reports 62 days&lt;/a&gt; as the global average for engineering roles. &lt;a href="https://www.recruitingfromscratch.com/blog/how-long-does-it-take-to-hire-a-senior-software-engineer-in-2026" rel="noopener noreferrer"&gt;Recruiting from Scratch&lt;/a&gt; says 47 days from opening a role to an accepted offer for senior engineers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notice period:&lt;/strong&gt; &lt;a href="https://fabrichq.ai/blogs/notice-period" rel="noopener noreferrer"&gt;two weeks is the usual custom in the US&lt;/a&gt;. In India, &lt;a href="https://www.zoho.com/in/payroll/academy/hr-and-payroll/notice-period.html" rel="noopener noreferrer"&gt;60 to 90 days is common at tech companies&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning your product:&lt;/strong&gt; 2 to 4 weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First public work:&lt;/strong&gt; depends on what you focus on. A docs fix or a tutorial can go live in the first few weeks, while community and event work takes longer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results you can measure:&lt;/strong&gt; often 3 to 6 months out. &lt;a href="https://www.stackmatix.com/blog/developer-marketing-agency" rel="noopener noreferrer"&gt;Stackmatix says to expect roughly one to two quarters before meaningful activation results appear&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agency timeline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kickoff and audit:&lt;/strong&gt; 2 to 4 weeks. Ask for dates in writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First content live:&lt;/strong&gt; within 1 to 2 weeks of kickoff. &lt;a href="https://infrasity.com/" rel="noopener noreferrer"&gt;Infrasity lists 7 days for a technical video&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Early signals:&lt;/strong&gt; around 90 days. &lt;a href="https://blog.stateshift.com/devrel-roi-metrics-how-to-measure-communitys-business-value/" rel="noopener noreferrer"&gt;Stateshift says most programs show early impact within 90 days&lt;/a&gt; if you track the right things, such as how many people finish a quickstart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community trust:&lt;/strong&gt; longer. An agency can ship tutorials fast, but trust in a community still builds slowly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When does an in-house DevRel hire make sense?
&lt;/h2&gt;

&lt;p&gt;Hire in-house when most of these are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your product is complex and changes often.&lt;/strong&gt; Someone has to sit in engineering conversations and understand why things work the way they do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You have a real community to look after.&lt;/strong&gt; A Discord, a GitHub project, or a forum needs someone who shows up every day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can wait 3 to 6 months for proof.&lt;/strong&gt; Set early milestones for onboarding and delivery, alongside longer-term goals for adoption and community development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can protect a budget line.&lt;/strong&gt; A person with no budget has little to work with and is easy to cut.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want developer feedback to reach your product team.&lt;/strong&gt; An employee can participate directly in product planning. External practitioners need agreed access and a clear feedback process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When does a DevRel agency make sense?
&lt;/h2&gt;

&lt;p&gt;An agency is the better choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need docs, tutorials, or comparison pages in weeks.&lt;/strong&gt; Waiting for a hire means waiting for the content too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are not sure DevRel will work for you.&lt;/strong&gt; A 3- to 6-month contract is a cheaper experiment than a $195,000 hire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You have a short push.&lt;/strong&gt; A launch, a new SDK, a hackathon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot find or afford a senior person.&lt;/strong&gt; In the &lt;a href="https://draft.dev/2026-developer-marketing-survey" rel="noopener noreferrer"&gt;Draft.dev 2026 developer marketing survey&lt;/a&gt;, 42% of teams said they outsource content creation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want steady routine work done well.&lt;/strong&gt; Checking docs for gaps, refreshing old posts, tracking what AI tools say about you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agencies have weak spots too. They do not know your product the way an employee does, and some put junior staff on your account after a senior pitch. &lt;a href="https://marketerhire.com/blog/developer-marketing-agency" rel="noopener noreferrer"&gt;MarketerHire argues&lt;/a&gt; that one senior fractional hire can beat an agency that spreads junior people across your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you use both?
&lt;/h2&gt;

&lt;p&gt;Yes, and this is where many teams land. One person inside the company owns strategy, community, and product feedback. An agency handles production: tutorials, docs fixes, sample apps, and comparison pages.&lt;/p&gt;

&lt;p&gt;Agencies sell this setup too. Hackmamba, for example, offers &lt;a href="https://hackmamba.io/pricing" rel="noopener noreferrer"&gt;a marketing leader from $10,000 a month&lt;/a&gt; to set strategy, priorities, and reporting for your existing team. We agree with the idea. The person who knows your product decides what to make. The agency makes more of it, faster. At Studio1, we work as an embedded partner for product teams, handling docs, tutorials, open-source upkeep, and community replies.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does AI search change the decision?
&lt;/h2&gt;

&lt;p&gt;A developer who used to search Google for "best error tracking tool for Rails" now often asks ChatGPT, Claude, or Gemini and reads one answer. &lt;a href="https://hackernoon.com/the-dev-tool-aeo-checklist" rel="noopener noreferrer"&gt;HackerNoon's dev tool checklist&lt;/a&gt; describes the moment when that answer names your competitor and not you. Getting named inside the answer is called answer engine optimization, or AEO.&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%2Fx934vhx9ft8g9aep1vn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx934vhx9ft8g9aep1vn4.png" alt="Image-2" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few facts worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sourced, specific writing gets picked up more.&lt;/strong&gt; Researchers from Princeton, Georgia Tech, and IIT Delhi found that adding citations, quotes from credible sources, and statistics &lt;a href="https://arxiv.org/abs/2311.09735" rel="noopener noreferrer"&gt;raised a page's visibility in AI answers by up to 40%&lt;/a&gt;. That is a lab result, not a promise for your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each AI tool answers differently.&lt;/strong&gt; One agency's research found only 12% of citations overlap across &lt;a href="https://www.gtm8020.com/blog/aeo-agencies-dev-tools-api-companies" rel="noopener noreferrer"&gt;ChatGPT, Perplexity, Gemini, and Claude&lt;/a&gt;. Check each one yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answers change between asks.&lt;/strong&gt; The same question can produce a different answer an hour later, so be wary of anyone who guarantees a ChatGPT ranking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search itself is shifting.&lt;/strong&gt; In 2024, Gartner predicted that &lt;a href="https://www.cmswire.com/digital-experience/will-search-engine-traffic-really-drop-25-by-2026-as-gartner-predicts/" rel="noopener noreferrer"&gt;traditional search volume would fall 25% by 2026&lt;/a&gt; as people move to AI chatbots. We have not seen a final measurement, so treat it as a prediction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A plain-text docs file may help coding tools.&lt;/strong&gt; The HackerNoon checklist says Google does not use &lt;code&gt;llms.txt&lt;/code&gt; and most of those files see no crawler traffic, but it is still worth shipping for developer docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most AEO work for developer tools comes down to three jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear docs, comparison pages, and tutorials on your own site&lt;/strong&gt;, with real numbers and sources. An agency can produce a lot of this quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest answers where developers already talk&lt;/strong&gt;, like GitHub, Reddit, and community forums. This needs real credibility, which leans toward an in-house hire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular checks on what each AI tool says about you&lt;/strong&gt;, then fixing the gaps. A junior teammate or an agency can own this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So AEO does not push you hard in either direction. It makes the hybrid setup more attractive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which option fits your stage?
&lt;/h2&gt;

&lt;p&gt;These are our opinions, built on the cost data above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-seed or seed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; About $2,000 to $5,000 a month for part-time help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best fit:&lt;/strong&gt; A solo DevRel consultant or a small content retainer. Jozu was at an early stage when we started working with them in October 2024. We began with developer content, community work, and events for their open-source project, KitOps, and kept going for just over a year. You can read the &lt;a href="https://www.studio1hq.com/case-studies/jozu" rel="noopener noreferrer"&gt;Jozu case study&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First 90 days:&lt;/strong&gt; Fix your quickstart, write five tutorials that answer real questions, and check what AI tools say about you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Series A
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; One hire at about $195,000 a year, or a retainer of $8,000 to $15,000 a month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best fit:&lt;/strong&gt; An agency if you need output now. A first hire if you already have a community and a clear owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First 90 days:&lt;/strong&gt; Pick one number to move, such as how many new signups make their first successful API call. Check it every week and fix the step where most people drop off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Series B and later
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; An in-house lead plus a retainer in the $8,000 to $15,000 range, more if several channels are involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best fit:&lt;/strong&gt; The hybrid model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First 90 days:&lt;/strong&gt; Set up reporting that ties DevRel work to signups and revenue, so the program survives the next budget round.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Open source projects
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; Usually small, often tied to a company sponsor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best fit:&lt;/strong&gt; Someone in-house who is already part of the project. Bring in an agency only for docs or specific pushes. We help open source teams with this kind of work. Our &lt;a href="https://www.studio1hq.com/case-studies/memori" rel="noopener noreferrer"&gt;Memori case study&lt;/a&gt; covers a launch that went from 0 to 12,000+ GitHub stars, and our &lt;a href="https://www.studio1hq.com/case-studies/corsair" rel="noopener noreferrer"&gt;Corsair case study&lt;/a&gt; covers developer experience, demos, and community growth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First 90 days:&lt;/strong&gt; Cut the time it takes a new contributor to get a first pull request merged.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What mistakes do teams make when choosing?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hiring before defining success.&lt;/strong&gt; In the 2024 State of Developer Relations survey, &lt;a href="https://stateofdeveloperrelations.com/2024devrelreport" rel="noopener noreferrer"&gt;33.5% of practitioners could not state their pro&lt;/a&gt;&lt;a href="https://www.skillsdirectory.com/skills/samber-devrel-strategy" rel="noopener noreferrer"&gt;gram budget, and another 17.8% had no set budget&lt;/a&gt;. A role with no goal and no budget is the first one cut.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judging too early.&lt;/strong&gt; Reviewing a new hire at month three punishes the work that pays off at month nine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counting vanity numbers.&lt;/strong&gt; Followers and page views look good and prove little. Quickstart completions and signups are harder to fake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signing a long agency contract with no exit.&lt;/strong&gt; Ask what leaving after three months costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Giving an agency nobody to talk to.&lt;/strong&gt; Without an internal owner, the agency guesses at your product, and the work shows it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treating DevRel as a support desk.&lt;/strong&gt; Answering tickets matters, but it is not a strategy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should you ask before you sign or hire?
&lt;/h2&gt;

&lt;p&gt;If you are choosing an agency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What proof do you have from a company like mine?&lt;/strong&gt; Ask for numbers you can check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is not in the monthly price?&lt;/strong&gt; Get the exclusions in writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who owns the content and code when we stop?&lt;/strong&gt; Settle this before the first invoice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are hiring a person:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Can they write and code?&lt;/strong&gt; Many are strong at only one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can they build a community and create content?&lt;/strong&gt; Developers expect good docs, tutorials, and quick replies on Discord or GitHub. Ask to see examples of each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have they built something developers still use?&lt;/strong&gt; A public track record beats a polished resume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What budget and goals do they get in writing?&lt;/strong&gt; And does their manager understand the job?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much does a DevRel agency cost?
&lt;/h3&gt;

&lt;p&gt;Pricing depends on scope, seniority, and allocated capacity. SaaSHero estimates DevRel agency retainers at $8,000–$28,000 per month, but this is a vendor estimate rather than an industry-wide average. Get a quote that specifies deliverables, exclusions, and contract terms.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does it cost to hire a developer advocate?
&lt;/h3&gt;

&lt;p&gt;Costs vary by location, experience, and responsibilities. In our illustrative US scenario, a $136,300 base salary becomes approximately $194,700 when applying the BLS private-industry compensation ratio. This is a budgeting example, not a verified average. Recruitment and program expenses are additional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a DevRel agency cheaper than hiring in-house?
&lt;/h3&gt;

&lt;p&gt;It can be, depending on scope and total costs. Our illustrative $195,000 hiring scenario equals $16,250 per month before recruitment and program expenses. Compare this with the agency’s total cost over the same period, including excluded expenses and internal oversight.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to see results from DevRel?
&lt;/h3&gt;

&lt;p&gt;Timelines depend on the product, starting point, and goals. Use the first 90 days to assess delivery, onboarding improvements, and initial developer responses. Set separate milestones for longer-term outcomes such as retention, community participation, and revenue contribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can an agency replace a Head of DevRel?
&lt;/h3&gt;

&lt;p&gt;An agency or fractional leader can provide strategy, program leadership, and product-feedback coordination if those responsibilities are included in the engagement. Your company still needs an accountable decision-maker for priorities, budget, and internal access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does DevRel help your product show up in ChatGPT and Claude answers?
&lt;/h3&gt;

&lt;p&gt;Yes, indirectly. Clear docs, honest comparisons, sourced numbers, and real answers in developer communities give AI tools better material to quote. Nobody can promise a specific ranking, because AI answers change from one ask to the next.&lt;/p&gt;

</description>
      <category>management</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>How to Measure Your Devtool's Visibility in AI Answers</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Thu, 15 Oct 2026 19:53:08 +0000</pubDate>
      <link>https://dev.to/studio1hq/how-to-measure-your-devtools-visibility-in-ai-answers-52h6</link>
      <guid>https://dev.to/studio1hq/how-to-measure-your-devtools-visibility-in-ai-answers-52h6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can find out how often AI tools recommend your devtool with a free spreadsheet audit that takes about two hours. You don't need paid software or code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write 12 questions a developer would really ask about your category, then ask each one three times in three AI tools. That gives you 108 answers to count.&lt;/li&gt;
&lt;li&gt;Count five things: how often you're named, how often you're in the first three, your share of all brand mentions, how often you're linked, and how often the AI gets you wrong.&lt;/li&gt;
&lt;li&gt;Ignore your exact position in the list. AI tools almost never give the same list twice, so position is mostly noise.&lt;/li&gt;
&lt;li&gt;Fix your biggest gap first, then run the same audit again next month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of developers now ask an AI before they ask Google. JetBrains surveyed more than 10,000 professional developers in January 2026 and found that &lt;a href="https://blog.jetbrains.com/research/2026/04/which-ai-coding-tools-do-developers-actually-use-at-work/" rel="noopener noreferrer"&gt;90% regularly used at least one AI tool at work&lt;/a&gt;. Stack Overflow's 2026 survey, with more than 30,000 responses from 169 countries, found that &lt;a href="https://itbrief.co.uk/story/stack-overflow-survey-shows-developers-trust-ai-with-caveats" rel="noopener noreferrer"&gt;only 17.2% of developers don't use AI tools&lt;/a&gt;. Its own history shows how fast that happened: &lt;a href="https://stackoverflow.blog/2026/10/01/a-look-back-before-we-look-forward-a-developer-survey-retrospective/" rel="noopener noreferrer"&gt;44% used AI tools in 2023, 62% in 2024, and 79% in 2025&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When one of those developers asks which tool to use, they don't get ten blue links. They get a short list of names. If yours isn't on it, they never see your docs, your pricing page or your launch post. And once the answer is on the page, fewer people click through. Pew Research tracked 900 US adults and found that in regular Google search, users clicked a result link in &lt;a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/" rel="noopener noreferrer"&gt;8% of visits when an AI summary appeared, versus 15% when it didn't&lt;/a&gt;. That's general search, not developer search, but the direction is clear.&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%2Fpqjmnzy2bn5aw0liv5i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqjmnzy2bn5aw0liv5i6.png" alt="Image1" width="800" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most devtool teams have no idea whether this is happening to them. Dashboards show traffic, GitHub stars, and signups. None of them show what an AI says when someone asks about your category. This guide gives you a simple way to find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AEO for developers?
&lt;/h2&gt;

&lt;p&gt;Answer engine optimization, or AEO, is the work of making your product easy for AI tools like ChatGPT, Claude, Gemini, Perplexity, and Google's AI Overviews to find, understand, and name when someone asks a question. You'll also see it called GEO or LLM SEO. They all mean roughly the same thing.&lt;/p&gt;

&lt;p&gt;For devtools, that mostly means clear docs, honest comparison pages, consistent facts and a real presence in the places developers talk. But you can't fix what you haven't looked at, so measuring comes first. That's what this guide covers.&lt;/p&gt;

&lt;p&gt;If you want the full fix list after your audit, Studio1 has a separate guide on &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;how to get your tool recommended by ChatGPT, Perplexity, and Google&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why can't you check AI answers just once?
&lt;/h2&gt;

&lt;p&gt;You might be tempted to ask ChatGPT one question, see your name, and call it a day. Don't.&lt;/p&gt;

&lt;p&gt;SparkToro and Gumshoe.ai had 600 volunteers run 12 prompts through ChatGPT, Claude, and Google's AI nearly 3,000 times. The odds of getting the same list of brands twice were &lt;a href="https://searchengineland.com/ai-recommendation-lists-rarely-repeat-study-468076" rel="noopener noreferrer"&gt;under 1 in 100, and the odds of the same order were closer to 1 in 1,000&lt;/a&gt;. Two fair warnings: &lt;a href="https://www.warc.com/content/feed/llms-produce-random-results-when-listing-brand-recommendations/en-GB/11352" rel="noopener noreferrer"&gt;the study wasn't peer-reviewed, and Gumshoe sells AI tracking&lt;/a&gt;. Still, it matches what you'll see yourself in ten minutes of testing.&lt;/p&gt;

&lt;p&gt;The useful part is what stayed steady. The three most mentioned brands in each category &lt;a href="https://communicateonline.me/news/ai-brand-recommendations-vary-marketers-warned-on-tracking-tools/" rel="noopener noreferrer"&gt;showed up in about 64% of ChatGPT answers, 73% of Claude answers, and 68% of Google AI answers&lt;/a&gt;. The order bounces around, but the same names keep coming back. That gives us the rule for this whole audit: &lt;strong&gt;count how often you appear, not where you appear.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tools also disagree with each other. Ahrefs found that &lt;a href="https://www.searchenginejournal.com/ai-recommendations-change-with-nearly-every-query-sparktoro/566242" rel="noopener noreferrer"&gt;Google's AI Mode and AI Overviews cite different sources 87% of the time for the same query&lt;/a&gt;. A BrightEdge analysis found the overlap between the sources two engines cite &lt;a href="https://frase.io/blog/which-ai-engines-cite-which-sources" rel="noopener noreferrer"&gt;can be as low as 16%&lt;/a&gt;. So a good score in one tool tells you very little about another. Test each one on its own.&lt;/p&gt;

&lt;p&gt;They also read different places. Profound looked at 30 million citations between August 2024 and June 2025:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;What it leaned on most&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;Wikipedia, at nearly 48% of its top citations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perplexity&lt;/td&gt;
&lt;td&gt;Reddit, at almost half of its citations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google AI Overviews&lt;/td&gt;
&lt;td&gt;Reddit (21%), then YouTube (18.8%), Quora (14.3%) and LinkedIn (13%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://online.thedailystar.net/node/3956806" rel="noopener noreferrer"&gt;Profound study, as reported by The Daily Star&lt;/a&gt;. That data is more than a year old, so don't treat the exact percentages as current. The pattern is the point: your docs are not the only thing these tools read about you.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to audit your devtool's AI visibility: a 5 step playbook
&lt;/h2&gt;

&lt;p&gt;Here's what you need before you start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A spreadsheet.&lt;/li&gt;
&lt;li&gt;Free or paid access to ChatGPT, Gemini, and Perplexity. Add Claude if you want a fourth. Free tiers are fine, but note which plan you used, since answers can differ.&lt;/li&gt;
&lt;li&gt;About two hours. That's my estimate for 108 answers at roughly a minute each.&lt;/li&gt;
&lt;li&gt;Optional: a teammate who runs the same list, so you can compare notes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set up each tool the same way. Open a fresh chat for every prompt. If the tool has a memory or personalization setting, turn it off so your past chats about your own product don't tilt the answer. If web search is optional, turn it on, because that's how most people get current answers. Write down the date and the tool name in your sheet.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Write 12 prompts developers would actually type
&lt;/h3&gt;

&lt;p&gt;Sort them into four buckets, three prompts each. This follows the &lt;a href="https://percepture.com/geo-insights/ai-visibility-audit/" rel="noopener noreferrer"&gt;category, comparison, validation, and branded split that audit teams like Percepture use&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bucket&lt;/th&gt;
&lt;th&gt;Template&lt;/th&gt;
&lt;th&gt;Example for a job queue library&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discover&lt;/td&gt;
&lt;td&gt;What's the best [category] for [language or framework]?&lt;/td&gt;
&lt;td&gt;What's the best job queue for Node.js?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discover&lt;/td&gt;
&lt;td&gt;Which [category] should a small team use for [use case]?&lt;/td&gt;
&lt;td&gt;Which job queue should a small team use for email sending?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discover&lt;/td&gt;
&lt;td&gt;I need to [job to be done]. What tools can I use?&lt;/td&gt;
&lt;td&gt;I need to retry failed background tasks. What tools can I use?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare&lt;/td&gt;
&lt;td&gt;[Your tool] vs [competitor]: which is better for [use case]?&lt;/td&gt;
&lt;td&gt;Acme Queue vs BullMQ: which is better for scheduled jobs?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare&lt;/td&gt;
&lt;td&gt;What are good alternatives to [competitor]?&lt;/td&gt;
&lt;td&gt;What are good alternatives to BullMQ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare&lt;/td&gt;
&lt;td&gt;[Category] tools compared: pros and cons&lt;/td&gt;
&lt;td&gt;Node.js job queues compared: pros and cons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How-to&lt;/td&gt;
&lt;td&gt;How do I [task] with [framework]?&lt;/td&gt;
&lt;td&gt;How do I retry failed jobs with Express?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How-to&lt;/td&gt;
&lt;td&gt;What's the easiest way to [task] without [pain]?&lt;/td&gt;
&lt;td&gt;What's the easiest way to run cron jobs without a separate server?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How-to&lt;/td&gt;
&lt;td&gt;Show me an example of [task] in [language]&lt;/td&gt;
&lt;td&gt;Show me an example of a delayed job in TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check&lt;/td&gt;
&lt;td&gt;Is [your tool] good for [use case]?&lt;/td&gt;
&lt;td&gt;Is Acme Queue good for high volume?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check&lt;/td&gt;
&lt;td&gt;What are the downsides of [your tool]?&lt;/td&gt;
&lt;td&gt;What are the downsides of Acme Queue?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check&lt;/td&gt;
&lt;td&gt;How much does [your tool] cost, and is there a free plan?&lt;/td&gt;
&lt;td&gt;How much does Acme Queue cost?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write like a person talks.&lt;/strong&gt; Pew found that &lt;a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/" rel="noopener noreferrer"&gt;longer searches and searches phrased as questions or full sentences trigger AI summaries more often&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spend your effort on Discover and How-to.&lt;/strong&gt; That's where someone hasn't picked a tool yet. Check prompts mostly test whether the AI describes you correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swap the placeholders for your real category, languages, and rivals.&lt;/strong&gt; The same template works for any devtool, and it makes it easy to repeat the audit for each language or framework you support.&lt;/li&gt;
&lt;/ul&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%2F395lp6h0gvukxqid2erf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F395lp6h0gvukxqid2erf.png" alt="Image2" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Run every prompt three times in three tools
&lt;/h3&gt;

&lt;p&gt;That's 12 prompts × 3 tools × 3 runs = 108 answers. Set up one row per answer with these columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;li&gt;Tool&lt;/li&gt;
&lt;li&gt;Prompt and bucket&lt;/li&gt;
&lt;li&gt;Run number (1, 2, or 3)&lt;/li&gt;
&lt;li&gt;Mentioned? (yes or no)&lt;/li&gt;
&lt;li&gt;In the first three names? (yes or no)&lt;/li&gt;
&lt;li&gt;Linked to your site, docs, or repo? (yes or no)&lt;/li&gt;
&lt;li&gt;Competitors named&lt;/li&gt;
&lt;li&gt;Anything wrong? (pricing, features, versions, supported languages)&lt;/li&gt;
&lt;li&gt;Notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why three runs? The SparkToro team ran each prompt 60 to 100 times, and they said it's still an open question &lt;a href="https://www.searchenginejournal.com/ai-recommendations-change-with-nearly-every-query-sparktoro/566242" rel="noopener noreferrer"&gt;how many runs are enough to get reliable data&lt;/a&gt;. Three is a floor, not a science. If you have the energy for five, do five. They also left open whether results from an API match what you see in the chat window, so run these by hand in the apps your customers use.&lt;/p&gt;

&lt;p&gt;Paste each answer into a second tab as you go. You'll want to reread a few of them later.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Score the answers
&lt;/h3&gt;

&lt;p&gt;You only need five numbers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mention rate.&lt;/strong&gt; Answers that name you, divided by all answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top-three rate.&lt;/strong&gt; Answers where you're among the first three names, divided by all answers. &lt;a href="https://www.searchenginejournal.com/measure-brand-ai-visibility-hubspot-spa/588040/" rel="noopener noreferrer"&gt;HubSpot's guide calls this recommendation rate&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share of voice.&lt;/strong&gt; Your mentions divided by every brand mention in all the answers, yours and your rivals combined. &lt;a href="https://www.triplewhale.com/blog/audit-brand-ai-visibility" rel="noopener noreferrer"&gt;Triple Whale gives the same formula&lt;/a&gt;: your mentions ÷ total category mentions. Think of it as your slice of the pie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link rate.&lt;/strong&gt; Answers that link to your own site, docs, or repo, divided by all answers. This matters because &lt;a href="https://itbrief.co.uk/story/stack-overflow-survey-finds-developers-wary-of-ai-tools" rel="noopener noreferrer"&gt;78.7% of developers say source attribution matters&lt;/a&gt; when they judge an AI answer, and only 6.6% would trust AI with an important decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy.&lt;/strong&gt; How many of the answers that name you get something wrong?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Calculate each one per tool and per bucket, not just overall. The overall number hides the useful stuff.&lt;/p&gt;

&lt;p&gt;A note on position: skip it. If you came here wanting to know whether you're “number two” in ChatGPT, the research says that number is close to meaningless, because the order shuffles on almost every run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A worked example with made-up numbers.&lt;/strong&gt; Here's what a finished audit might look like for Acme Queue, a pretend job queue library. These numbers are invented to show the math.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;Mentioned&lt;/th&gt;
&lt;th&gt;Mention rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perplexity&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All three&lt;/td&gt;
&lt;td&gt;108&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Acme Queue was in the first three names in 14 answers (13%), linked in 9 (8%), and got something wrong in 4 of the 27 answers that named it (15%). Across all 108 answers, there were 270 brand mentions in total, so its share of voice was 27 ÷ 270 = 10%.&lt;/p&gt;

&lt;p&gt;Now split it into buckets. Each bucket has 27 answers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bucket&lt;/th&gt;
&lt;th&gt;Mentioned&lt;/th&gt;
&lt;th&gt;Mention rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discover&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How-to&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's a very common shape. People who already know the product get a decent answer. People who don't know it basically never meet it. The fix for that is different from the fix for wrong facts, which is why the bucket split matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Read the results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;th&gt;What it probably means&lt;/th&gt;
&lt;th&gt;Your first move&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rarely named, even in Check prompts&lt;/td&gt;
&lt;td&gt;The AI barely knows you exist&lt;/td&gt;
&lt;td&gt;Get your product described in more places: your docs, comparison pages, community threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named when asked by name, missing in Discover prompts&lt;/td&gt;
&lt;td&gt;People who know you find you, strangers don't&lt;/td&gt;
&lt;td&gt;Publish pages that match category questions, and earn mentions on third-party sites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named, but details are wrong&lt;/td&gt;
&lt;td&gt;Old or conflicting info is out there&lt;/td&gt;
&lt;td&gt;Fix your docs, pricing page and changelog first, then the outdated third-party posts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rivals win the Compare prompts&lt;/td&gt;
&lt;td&gt;Nobody has written your side of the comparison&lt;/td&gt;
&lt;td&gt;Publish an honest comparison page and join the comparison threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named, but the link goes to a forum or blog, not you&lt;/td&gt;
&lt;td&gt;The AI trusts other people's write-ups more than yours&lt;/td&gt;
&lt;td&gt;Put a clear, direct answer at the top of the matching docs page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong in one tool, weak in another&lt;/td&gt;
&lt;td&gt;Normal, because they read different sources&lt;/td&gt;
&lt;td&gt;Look at what the strong tool cites and learn from it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When a tool shows its sources, click through them. That list is a to-do list. It tells you which pages the AI trusts on your topic, and whether you're on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Fix the biggest gap first
&lt;/h3&gt;

&lt;p&gt;Pick one gap and work on it for a month. Here's the short version of what to do next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let the right bots in.&lt;/strong&gt; AI tools can only use what their crawlers (the bots that read websites) can reach. OpenAI says its &lt;a href="https://developers.openai.com/docs/gptbot" rel="noopener noreferrer"&gt;OAI-SearchBot is for search and isn't used to train its models, while GPTBot is used for training, and each is controlled separately in robots.txt&lt;/a&gt;. OpenAI recommends allowing OAI-SearchBot if you want to appear in ChatGPT search, and says changes can take about 24 hours to show up. Your robots.txt file is a small text file at the root of your site. A simple version looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;-agent: OAI-SearchBot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="nc"&gt;User&lt;/span&gt;-agent: PerplexityBot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whether to allow training crawlers like GPTBot is a separate decision, and it's yours. Other companies publish their own bot names too, and they change, so check each one's docs. &lt;a href="https://superblog.ai/blog/ai-crawlers-guide/" rel="noopener noreferrer"&gt;This crawler guide&lt;/a&gt; is a decent starting list. Also check your hosting firewall or bot protection, since those can block unfamiliar bots even when robots.txt says yes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer the question in the first lines of the page.&lt;/strong&gt; If someone asks, “How do I retry failed jobs in Express?”, your docs page should open with that answer and a short code sample, before any backstory. One analysis of Perplexity found that for technical questions, &lt;a href="https://freelanceseo.sg/blog/perplexity-citation-patterns/" rel="noopener noreferrer"&gt;its citations lean toward official documentation, GitHub issue threads, and posts from the engineers behind the tools&lt;/a&gt;. That's one source, so treat it as a hint, but it fits the Profound data above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Publish an honest comparison page.&lt;/strong&gt; Say who your tool is great for and who should pick something else. Pages like that are easy for an AI to quote and easy for a developer to trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Show up where developers already talk.&lt;/strong&gt; Given how much Reddit, YouTube, and community content the tools lean on, a helpful answer in a real thread can matter more than another blog post. Be honest about who you are, and answer the actual question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep your facts consistent and dated.&lt;/strong&gt; Pricing, versions, supported languages, and limits should match on your site, your docs, your repo README and your changelog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-run the audit every month.&lt;/strong&gt; Same prompts, same sheet. Because answers shift from run to run, a change of a few points is probably noise. Look for movement that holds for two or three months. That last bit is my advice, not a research finding.&lt;/p&gt;

&lt;p&gt;For the full playbook on each of these, read Studio1's guide on &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;getting your tool recommended by ChatGPT, Perplexity, and Google&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What mistakes ruin an AI visibility audit?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only testing prompts with your name in them.&lt;/strong&gt; You'll feel great and learn nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running each prompt once.&lt;/strong&gt; One answer is a coin flip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using your logged-in chat with years of history.&lt;/strong&gt; Your past conversations can tilt the answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blending all tools into one number.&lt;/strong&gt; Keep a score for each tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chasing position.&lt;/strong&gt; Count appearances instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring accuracy.&lt;/strong&gt; Being named with the wrong price or an old API is sometimes worse than not being named.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is AEO for developers?
&lt;/h3&gt;

&lt;p&gt;AEO, short for answer engine optimization, is the work of making sure AI tools like ChatGPT, Claude, Gemini, and Perplexity find, understand, and recommend your developer product. For devtools, that mostly means clear docs, honest comparisons, consistent facts, and a real presence in the communities developers use.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I check whether ChatGPT recommends my devtool?
&lt;/h3&gt;

&lt;p&gt;Write 12 questions a developer would ask about your category, ask each one three times in a fresh chat, and count how often you're named. The playbook above walks through it, and the whole thing takes about two hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  How many times should I run each prompt?
&lt;/h3&gt;

&lt;p&gt;At least three. Researchers who ran prompts 60 to 100 times saw almost no two answers match, and nobody has settled on how many runs are enough. More runs give you steadier numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I track my position in the list?
&lt;/h3&gt;

&lt;p&gt;No. In SparkToro's study, the same list in the same order came up closer to 1 time in 1,000. Track how often you appear instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need a paid AI visibility tool?
&lt;/h3&gt;

&lt;p&gt;Not to start. A spreadsheet is enough for a first baseline. Paid tools save time later by running prompts on a schedule, but ask any vendor how many times they run each prompt before they report a number.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should I re-run the audit?
&lt;/h3&gt;

&lt;p&gt;Monthly works well for most small teams. Keep the prompts the same so the numbers are comparable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I block GPTBot?
&lt;/h3&gt;

&lt;p&gt;That's a separate choice from showing up in ChatGPT search. Per OpenAI's docs, GPTBot is used for training, and OAI-SearchBot is used for search, and you can allow one and block the other.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>analytics</category>
      <category>llm</category>
      <category>seo</category>
    </item>
    <item>
      <title>llms.txt Explained: What It Is, Whether Developer Tools Need One, and a Template</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:32:01 +0000</pubDate>
      <link>https://dev.to/studio1hq/llmstxt-explained-what-it-is-whether-developer-tools-need-one-and-a-template-2c8m</link>
      <guid>https://dev.to/studio1hq/llmstxt-explained-what-it-is-whether-developer-tools-need-one-and-a-template-2c8m</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;llms.txt is a plain Markdown file that tells AI tools what to do and which pages matter most. Jeremy Howard proposed it in September 2024. It's a community proposal, not an official standard.&lt;/li&gt;
&lt;li&gt;It won't get you recommended. Google says its search ignores it, a study of about 300,000 domains found no link to AI citations, and Ahrefs found 97% of files got zero requests in May 2026.&lt;/li&gt;
&lt;li&gt;Developer tools should still add one to their docs. It takes about an hour; developers do point coding assistants at these files, and Chrome's Lighthouse now checks for one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A developer pastes your docs link into a coding assistant and says, "set this up for me." The assistant has to dig the useful text out of menus, scripts, and banners. Sometimes it grabs an old page and writes code for a version you retired two years ago. llms.txt tries to fix that.&lt;/p&gt;

&lt;p&gt;It's cheap, it's useful in one specific place, and it won't do what most blog posts promise. For the bigger picture, see our guide on &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;how to get your tool recommended by ChatGPT, Perplexity, and Google&lt;/a&gt;. This post goes deep on one file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is llms.txt?
&lt;/h2&gt;

&lt;p&gt;llms.txt is a Markdown file at &lt;code&gt;/llms.txt&lt;/code&gt; on your website. It gives AI tools a short description of your product and a hand-picked list of links, each with a one-line note. Think of it as a table of contents written for software.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;Jeremy Howard&lt;/a&gt;, co-founder of Answer.AI and fast.ai, proposed it on September 3, 2024, and published a rewrite (v2) in August 2026. The AI labs use it too: the spec points to llms.txt files for the developer docs of &lt;a href="https://developers.openai.com/llms.txt" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://docs.anthropic.com/llms.txt" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;, and &lt;a href="https://ai.google.dev/gemini-api/docs/llms.txt" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why it exists: web pages are built for people, so an AI tool gets the content plus a pile of layout code. A short index with links to clean pages saves it time.&lt;/p&gt;

&lt;p&gt;Version 2 &lt;a href="https://llmstxt.org/changes.html" rel="noopener noreferrer"&gt;changed three things&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Links should point to Markdown pages.&lt;/strong&gt; A page at &lt;code&gt;/docs/api&lt;/code&gt; can have a plain-text twin at &lt;code&gt;/docs/api.md&lt;/code&gt; (or &lt;code&gt;page.html.md&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages can point to their llms.txt.&lt;/strong&gt; Use link tags or an HTTP header: &lt;code&gt;rel="alternate" type="text/markdown"&lt;/code&gt; for the Markdown copy, &lt;code&gt;rel="describedby"&lt;/code&gt; for the llms.txt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A file can live in a subfolder.&lt;/strong&gt; &lt;code&gt;/docs/llms.txt&lt;/code&gt; covers everything under &lt;code&gt;/docs/&lt;/code&gt;, and the most specific file wins.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What does an llms.txt file look like?
&lt;/h2&gt;

&lt;p&gt;According to the &lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;spec&lt;/a&gt;, a file has these parts, in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An H1 with your project or site name. &lt;strong&gt;The only required part.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A blockquote with a short summary of the key facts.&lt;/li&gt;
&lt;li&gt;Optional paragraphs or lists (no headings) with extra detail.&lt;/li&gt;
&lt;li&gt;Optional H2 sections, each a list of links: &lt;code&gt;[Name](url): short note&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By convention, a section called &lt;code&gt;Optional&lt;/code&gt; holds links a tool can skip when short on room. The smallest useful version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# ExampleDB&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; ExampleDB is a hosted Postgres database for small teams.&lt;/span&gt;

&lt;span class="gu"&gt;## Docs&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/quickstart.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Create a project and run a first query.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No special syntax, nothing to install.&lt;/p&gt;

&lt;h2&gt;
  
  
  llms.txt vs robots.txt vs sitemap.xml: what's the difference?
&lt;/h2&gt;

&lt;p&gt;They do different jobs. llms.txt replaces neither of the others.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Who reads it&lt;/th&gt;
&lt;th&gt;Can it block anyone?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;robots.txt&lt;/td&gt;
&lt;td&gt;Says which bots may visit which pages&lt;/td&gt;
&lt;td&gt;Search and AI crawlers&lt;/td&gt;
&lt;td&gt;Yes, if the bot follows the rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sitemap.xml&lt;/td&gt;
&lt;td&gt;Lists every page you want indexed&lt;/td&gt;
&lt;td&gt;Search engines&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llms.txt&lt;/td&gt;
&lt;td&gt;A short, curated guide to your best pages&lt;/td&gt;
&lt;td&gt;AI tools and coding assistants, when they choose to fetch it&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A sitemap lists everything and is usually too big for an AI tool to read in one go. The spec calls &lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;llms.txt a curated overview&lt;/a&gt;. To keep a bot out, use robots.txt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does AI actually read llms.txt? What the data says
&lt;/h2&gt;

&lt;p&gt;Mostly no, and several independent looks agree.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Study&lt;/th&gt;
&lt;th&gt;What they looked at&lt;/th&gt;
&lt;th&gt;What they found&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ahrefs.com/blog/llmstxt-study/" rel="noopener noreferrer"&gt;Ahrefs&lt;/a&gt;, May 2026&lt;/td&gt;
&lt;td&gt;137,210 domains&lt;/td&gt;
&lt;td&gt;28% published a valid file. 97% of those got zero requests all month. About 96% of requests that did arrive came from bots.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://seranking.com/blog/llms-txt/" rel="noopener noreferrer"&gt;SE Ranking&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;About 300,000 domains&lt;/td&gt;
&lt;td&gt;10.13% had the file. No measurable link to how often a domain was cited by AI tools.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://somethinginc.com/blog/llms-txt-ai-crawlers-fetch-data/" rel="noopener noreferrer"&gt;EZY Research&lt;/a&gt;, 12 weeks&lt;/td&gt;
&lt;td&gt;The same valid file on 83 sites&lt;/td&gt;
&lt;td&gt;OpenAI's crawler fetched llms.txt 7 times vs 3,990 for robots.txt. ClaudeBot: 9 vs 3,120. PerplexityBot: 0 vs 775. Meta's crawler: 193 vs 172.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dejan.ai/blog/crawler-census-robots-llms-okf/" rel="noopener noreferrer"&gt;Dejan.ai&lt;/a&gt;, 27 July to 15 August 2026&lt;/td&gt;
&lt;td&gt;One site's server logs&lt;/td&gt;
&lt;td&gt;Google, Anthropic, and OpenAI crawlers fetched robots.txt 1,150 times and llms.txt 4 times.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ppc.land/only-7-4-of-fortune-500-have-an-llms-txt-file-study-finds/" rel="noopener noreferrer"&gt;ProGEO.ai&lt;/a&gt;, March 2026&lt;/td&gt;
&lt;td&gt;The Fortune 500&lt;/td&gt;
&lt;td&gt;37 of 500 (7.4%) had an llms.txt. 92.8% had a robots.txt.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What stands out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adoption numbers differ because samples differ.&lt;/strong&gt; Ahrefs measured sites using its analytics, which skew technical. Don't quote one number as "the" rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Busy crawlers mostly skip the file.&lt;/strong&gt; In the EZY test, big crawlers read robots.txt thousands of times and llms.txt almost never. Meta's crawler was the lone exception.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI bots don't go hunting for it.&lt;/strong&gt; Requests for llms.txt files that didn't exist drew no AI bot traffic. Of requests to real files, about 19.5% came from named AI tools and 12% from tools that scan or audit these files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google is clear about search.&lt;/strong&gt; Its &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;generative AI search guide&lt;/a&gt; (updated July 10, 2026) lists llms.txt as something you can ignore. Keeping one neither helps nor hurts. John Mueller has &lt;a href="https://www.searchenginejournal.com/googles-mueller-llms-txt-is-comparable-to-the-keywords-meta-tag/544804/" rel="noopener noreferrer"&gt;compared it to the old keywords meta tag&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yet Google built a check for it.&lt;/strong&gt; On May 5, 2026, Chrome added an llms.txt audit to &lt;a href="https://developer.chrome.com/docs/lighthouse/agentic-browsing/llms-txt" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; under "Agentic browsing audits." It only fails if your server errors on the request. No file means "Not Applicable." Search ranking is one job. Helping an AI agent browse for a user is another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One caution.&lt;/strong&gt; Per &lt;a href="https://searchenginejournal.com/97-of-llms-txt-files-got-no-requests-ahrefs-data-shows/579478" rel="noopener noreferrer"&gt;Search Engine Journal's summary&lt;/a&gt;, Ahrefs saw a research crawler studying llms.txt as a way to slip instructions to AI tools. Stick to your own plain facts and links.&lt;/li&gt;
&lt;/ul&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%2Fh51826hjlj1onyd15eh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh51826hjlj1onyd15eh3.png" alt="Image1" width="800" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do developer tools need an llms.txt file?
&lt;/h2&gt;

&lt;p&gt;Need? No. Should you make one? For your docs, yes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It won't help you rank or get cited.&lt;/strong&gt; If someone says a 30-minute llms.txt will get you into ChatGPT's answers, ask for their data. What moves the needle is in our &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;AEO playbook for developer tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It does help people using coding assistants.&lt;/strong&gt; Developers already point their tools at these files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google's Agent Development Kit docs &lt;a href="https://google.github.io/adk-docs/tutorials/coding-with-ai" rel="noopener noreferrer"&gt;show how to point Gemini CLI, Claude Code, and Cursor at their llms.txt&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Bright Data's docs tell developers to &lt;a href="https://docs.brightdata.com/quickstart-coding-agent.md" rel="noopener noreferrer"&gt;save its llms.txt into their project&lt;/a&gt;, in a Cursor rules file or an &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Vercel publishes &lt;a href="https://vercel.com/docs/agent-resources" rel="noopener noreferrer"&gt;an llms.txt and an llms-full.txt&lt;/a&gt; and explains how to use them with Claude Code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Howard says in v2 that coding assistants now use these files reliably. That's a smaller audience than "all of AI search," but it's the one a developer tool cares about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs almost nothing.&lt;/strong&gt; An hour of work, plus keeping it current.&lt;/p&gt;

&lt;p&gt;Put it where your docs live. A file at &lt;code&gt;docs.yourtool.com/llms.txt&lt;/code&gt; or &lt;code&gt;yourtool.com/docs/llms.txt&lt;/code&gt; can cover just the docs, and a second one at the root can cover the marketing 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%2Fgh40fbdykmrim8d32tan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgh40fbdykmrim8d32tan.png" alt="Image2" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example: the llms.txt file we use at Studio1
&lt;/h2&gt;

&lt;p&gt;Here is &lt;a href="https://www.studio1hq.com/llms.txt" rel="noopener noreferrer"&gt;our own file&lt;/a&gt;, trimmed to show the shape (first item kept in each section).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Studio1&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Technical content and developer growth partner for devtool, SaaS, and AI teams.&lt;/span&gt;

Studio1 helps developer-focused companies grow adoption through technical content
marketing, developer documentation and DX audits, developer video production, and
developer relations growth campaigns. [...]

&lt;span class="gu"&gt;## Services&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Technical Content Marketing&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://studio1hq.com/technical-content-marketing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Developer tutorials, integration guides, SEO articles, and code-rich content written by engineers.
&lt;span class="p"&gt;-&lt;/span&gt; [...]

&lt;span class="gu"&gt;## Key Pages&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Homepage&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://studio1hq.com/&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="nv"&gt;Client Work&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://studio1hq.com/work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Tutorials, guides, and developer content Studio1 creates for client teams.
&lt;span class="p"&gt;-&lt;/span&gt; [...]

&lt;span class="gu"&gt;## Products&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Raah&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://raah.dev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Web analytics and real-user observability for developers and SaaS teams.
&lt;span class="p"&gt;-&lt;/span&gt; [...]

&lt;span class="gu"&gt;## Blog&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;How to Get Your Tool Recommended by ChatGPT, Perplexity, and Google&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: AEO, GEO, LLM SEO, crawler access, documentation strategy, comparison pages, community mentions, schema, and llms.txt guidance for developer tools.
&lt;span class="p"&gt;-&lt;/span&gt; [...]

&lt;span class="gu"&gt;## Team&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Arindam Majumder**&lt;/span&gt;: Co-founder and Content Lead. [...]

&lt;span class="gu"&gt;## Contact&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Email: contact@studio1hq.com
&lt;span class="p"&gt;-&lt;/span&gt; [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it gets right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The H1 and quote answer "who is this?" in one sentence.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every link has a note&lt;/strong&gt; saying what's behind it, so a tool can pick the right page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The order follows what people ask:&lt;/strong&gt; services, then proof, then the blog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's short.&lt;/strong&gt; One or two screens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What we'd tighten (use it as a checklist for your own file):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Point links at Markdown pages.&lt;/strong&gt; Ours link to HTML pages. We tested one post URL with &lt;code&gt;.md&lt;/code&gt; added and it returns a 404, so the Markdown twins don't exist yet. This is the biggest gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use one hostname.&lt;/strong&gt; The file is served from &lt;code&gt;www.studio1hq.com&lt;/code&gt;, the links use &lt;code&gt;studio1hq.com&lt;/code&gt;. It redirects, so nothing breaks, but one hostname is cleaner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move low-value links under &lt;code&gt;Optional&lt;/code&gt;.&lt;/strong&gt; Terms, Privacy, and Careers don't belong in Key Pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep list sections as links.&lt;/strong&gt; Our Team and Contact bullets are plain text. Make them links or fold them into the intro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add new posts as you publish.&lt;/strong&gt; Our Blog section is hand-picked, which is fine if someone owns it. A stale file sends tools to pages that moved.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  An llms.txt template for developer tools
&lt;/h2&gt;

&lt;p&gt;Copy this and swap in your details. "ExampleDB" and "RivalDB" are made up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# ExampleDB&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; ExampleDB is a hosted Postgres database for small teams. It works with Prisma and Drizzle, and a new project takes about five minutes to set up.&lt;/span&gt;

Things to know before answering questions about ExampleDB:
&lt;span class="p"&gt;
-&lt;/span&gt; It runs Postgres 16. Standard Postgres SQL works.
&lt;span class="p"&gt;-&lt;/span&gt; The free plan pauses a project after 7 days with no activity. Paid plans never pause.
&lt;span class="p"&gt;-&lt;/span&gt; The Node package is &lt;span class="sb"&gt;`@exampledb/client`&lt;/span&gt;. Older tutorials that use &lt;span class="sb"&gt;`exampledb-js`&lt;/span&gt; are out of date.
&lt;span class="p"&gt;-&lt;/span&gt; ExampleDB does not support MySQL.

&lt;span class="gu"&gt;## Start here&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/quickstart.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Create a project and run your first query.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Connect with Prisma&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/guides/prisma.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Connection string, pooling, and migrations.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Connect with Drizzle&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/guides/drizzle.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Setup and a working schema example.

&lt;span class="gu"&gt;## API reference&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;REST API&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/api.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Every endpoint with request and response examples.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Node SDK&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/sdk/node.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Install, auth, and common calls.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Error codes&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/errors.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Each error with its cause and fix.

&lt;span class="gu"&gt;## Guides&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Migrate from RivalDB&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/migrate/rivaldb.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Export, import, and what changes.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Backups and restore&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/backups.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Schedules, retention, and point-in-time restore.

&lt;span class="gu"&gt;## Pricing and comparisons&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Pricing&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://exampledb.com/pricing.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Plans, limits, and what counts as usage.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ExampleDB vs RivalDB&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://exampledb.com/compare/rivaldb.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Features, pricing, and where RivalDB is the better fit.

&lt;span class="gu"&gt;## Optional&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Changelog&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.exampledb.com/changelog.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Release notes by version.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Status page&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://status.exampledb.com&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Current uptime and incidents.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Privacy policy&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://exampledb.com/privacy.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to fill it in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write the quote like an answer&lt;/strong&gt; to "what is ExampleDB?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a "things to know" list.&lt;/strong&gt; The most useful part for developer tools. Include renamed packages, retired versions, limits, and what you don't support. The spec's own FastHTML example does this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write each note as the question the page answers.&lt;/strong&gt; "Connection string, pooling, and migrations" beats "Prisma guide."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link to Markdown pages where you can.&lt;/strong&gt; If not yet, link to normal pages and add Markdown copies to your to-do list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put legal links in Optional.&lt;/strong&gt; Keep pricing and comparisons near the top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test it.&lt;/strong&gt; Per the spec, give an assistant only your llms.txt and ask it five real questions. If it can't answer, the file is missing something.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Do you need llms-full.txt too?
&lt;/h2&gt;

&lt;p&gt;Probably not at first. llms.txt is the table of contents. llms-full.txt is the whole book in one Markdown file.&lt;/p&gt;

&lt;p&gt;Bright Data's docs give a sensible rule: for a live assistant session, &lt;a href="https://docs.brightdata.com/quickstart-coding-agent.md" rel="noopener noreferrer"&gt;load llms.txt first and fetch single pages as needed&lt;/a&gt;. Use the full file for pipelines and tools that need everything at once. It can be very large. Suppose your docs platform generates both; great. If you write by hand, start with llms.txt.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to add llms.txt to your site
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your setup&lt;/th&gt;
&lt;th&gt;Easiest way&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mintlify docs&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.mintlify.com/docs/ai/llmstxt" rel="noopener noreferrer"&gt;Generated automatically&lt;/a&gt;, along with Markdown versions of every page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitBook&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.gitbook.com/blog/what-is-llms-txt" rel="noopener noreferrer"&gt;Serves one for published docs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docusaurus&lt;/td&gt;
&lt;td&gt;The &lt;a href="https://github.com/rachfop/docusaurus-plugin-llms" rel="noopener noreferrer"&gt;docusaurus-plugin-llms&lt;/a&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VitePress&lt;/td&gt;
&lt;td&gt;The &lt;a href="https://github.com/okineadev/vitepress-plugin-llms" rel="noopener noreferrer"&gt;vitepress-plugin-llms&lt;/a&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WordPress&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://yoast.com/features/llms-txt/" rel="noopener noreferrer"&gt;Yoast SEO&lt;/a&gt; or &lt;a href="https://aioseo.com/features/llms-txt/" rel="noopener noreferrer"&gt;AIOSEO&lt;/a&gt; generate and update it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wix&lt;/td&gt;
&lt;td&gt;&lt;a href="https://support.wix.com/en/article/understanding-your-sites-llmstxt-file" rel="noopener noreferrer"&gt;Generated for every site&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drupal 10.3 and up&lt;/td&gt;
&lt;td&gt;The &lt;a href="https://www.drupal.org/project/llm_support" rel="noopener noreferrer"&gt;LLM Support recipe&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js, Astro, or any static site&lt;/td&gt;
&lt;td&gt;Write it by hand and drop it in your public folder so it serves at &lt;code&gt;/llms.txt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you write it by hand, serve it as plain text with a 200. Lighthouse only complains about server errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check if anyone is reading your llms.txt
&lt;/h2&gt;

&lt;p&gt;Normal analytics won't show it, because AI tools fetch files without running your scripts. Use server logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"llms.txt"&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"gptbot|oai-searchbot|chatgpt-user|claudebot|claude-user|perplexitybot"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expect very little.&lt;/strong&gt; A handful of hits a month is normal. Zero is common.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the status code.&lt;/strong&gt; 200 means served. 404 or 500 means something's broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check robots.txt first.&lt;/strong&gt; If it blocks a crawler, that crawler never reaches your llms.txt. Our &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;AEO guide&lt;/a&gt; covers which bots to allow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger win is the manual test: give an assistant your llms.txt and a docs question, and see if the answer is right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common llms.txt mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dumping every URL into it.&lt;/strong&gt; That's a sitemap's job. Pick pages you'd show a new customer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting it up once and forgetting it.&lt;/strong&gt; Update it with your changelog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linking to pages behind a login.&lt;/strong&gt; Tools can't read them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using it to block bots.&lt;/strong&gt; It can't. Use robots.txt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expecting traffic.&lt;/strong&gt; Judge it by whether assistants answer your docs questions correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipping the "things to know" list.&lt;/strong&gt; It stops tools from repeating old, wrong advice about your product.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  What is llms.txt in simple terms?
&lt;/h3&gt;

&lt;p&gt;A plain text file at the root of your site with a short summary of your product and links to your best pages. It's Markdown, so people and software can both read it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is llms.txt an official standard?
&lt;/h3&gt;

&lt;p&gt;No. It's a community proposal from Jeremy Howard (September 2024, v2 in August 2026). No standards body has adopted it, and the big AI companies haven't publicly confirmed that their search tools rely on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does llms.txt help SEO?
&lt;/h3&gt;

&lt;p&gt;No. Google says its search ignores it, and a study of about 300,000 domains found no link between having one and being cited by AI tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does ChatGPT or Claude read llms.txt?
&lt;/h3&gt;

&lt;p&gt;Not reliably, per the logs. In a 12-week test on 83 sites, OpenAI's crawler read it 7 times, and Anthropic's 9, against thousands of robots.txt reads. Coding assistants do read it when a developer points them at it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between llms.txt and llms-full.txt?
&lt;/h3&gt;

&lt;p&gt;llms.txt is a short index of links with notes. llms-full.txt is the full text of your docs in one Markdown file. Use the index for live questions and the full file for tools that need everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where do I put llms.txt?
&lt;/h3&gt;

&lt;p&gt;At your site root, such as &lt;code&gt;https://example.com/llms.txt&lt;/code&gt;. Under v2, you can also add one in a subfolder like &lt;code&gt;/docs/llms.txt&lt;/code&gt;, covering pages under that path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can llms.txt block AI crawlers?
&lt;/h3&gt;

&lt;p&gt;No. It can't allow or deny anything. Use robots.txt.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should I update llms.txt?
&lt;/h3&gt;

&lt;p&gt;Whenever key pages change: a new doc section, a renamed package, a new pricing page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use a generator or write it by hand?
&lt;/h3&gt;

&lt;p&gt;If your docs platform makes one (Mintlify, GitBook, and others), use it and edit the intro. Otherwise, use the template above. Read the result once, since generated files often list too many pages.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Developer Docs as Your Best AEO Asset: How to Get AI to Cite Your Content</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Fri, 09 Oct 2026 14:20:34 +0000</pubDate>
      <link>https://dev.to/studio1hq/docs-as-your-best-aeo-asset-how-to-structure-developer-documentation-so-ai-assistants-cite-it-50cp</link>
      <guid>https://dev.to/studio1hq/docs-as-your-best-aeo-asset-how-to-structure-developer-documentation-so-ai-assistants-cite-it-50cp</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers now ask AI assistants before they open your docs, and machines already read a large share of doc pages.&lt;/li&gt;
&lt;li&gt;Assistants cite single passages, so each docs section needs to answer one question on its own.&lt;/li&gt;
&lt;li&gt;Plain HTML, answer-first writing, working code, visible versions, and fresh dates beat special files and extra markup.&lt;/li&gt;
&lt;li&gt;Authority from outside your site still decides who wins, so earn links and real mentions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of developers now ask an assistant before they search Google. In the &lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;2025 Stack Overflow Developer Survey&lt;/a&gt;, 50.6% of professional developers said they use AI tools every day, and another 17.4% use them weekly.&lt;/p&gt;

&lt;p&gt;That changes what your docs are for. They are no longer only a place people visit. They are the source an assistant reads, quotes, and cites when someone asks how to use your product.&lt;/p&gt;

&lt;p&gt;This guide shows how to structure developer documentation so assistants find it, trust it, and cite it. Every number below links to its source.&lt;/p&gt;

&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%2Fkj2p3ozlwjyr9y0ssrvi.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%2Fkj2p3ozlwjyr9y0ssrvi.webp" alt="Agent-vs-human" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is answer engine optimization (AEO) for developer docs?
&lt;/h2&gt;

&lt;p&gt;Answer engine optimization is the work of making your content easy for AI tools to retrieve, quote, and credit. For docs, that means writing pages so an assistant can pull out one correct, complete answer.&lt;/p&gt;

&lt;p&gt;It builds on normal SEO. The difference is the unit of competition. In classic search, the page ranks. In AI answers, a single passage gets picked. A clear section can win even when the page around it is average.&lt;/p&gt;

&lt;p&gt;Some people call this GEO, short for generative engine optimization. The tasks are the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do AI assistants decide which docs to cite?
&lt;/h2&gt;

&lt;p&gt;There are two kinds of readers hitting your docs, and they behave differently.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Search style assistants&lt;/th&gt;
&lt;th&gt;Coding assistants&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Examples&lt;/td&gt;
&lt;td&gt;ChatGPT search, Google AI Overviews and AI Mode, Gemini, Perplexity&lt;/td&gt;
&lt;td&gt;Cursor, Claude Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What they do&lt;/td&gt;
&lt;td&gt;Answer a question and attach links&lt;/td&gt;
&lt;td&gt;Fetch docs in the middle of a task so the code is right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who sees your page&lt;/td&gt;
&lt;td&gt;Sometimes a person who clicks&lt;/td&gt;
&lt;td&gt;Often nobody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What helps most&lt;/td&gt;
&lt;td&gt;Authority, clear passages, fresh pages&lt;/td&gt;
&lt;td&gt;Clean text, working code, easy to find pages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both follow a similar path. &lt;a href="https://visible.seranking.com/blog/answer-engine-optimization/" rel="noopener noreferrer"&gt;SE Ranking describes four steps&lt;/a&gt;: the system finds candidate pages, breaks each page into passages, picks the passages that answer most directly, and credits some of them.&lt;/p&gt;

&lt;p&gt;The data on what gets cited is useful. SE Ranking studied more than 2 million pages. These are links between traits and citations, not proof of cause, so read them as strong hints.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What they measured&lt;/th&gt;
&lt;th&gt;What they found&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page depth (ChatGPT)&lt;/td&gt;
&lt;td&gt;Pages over 2,900 words averaged 5.1 citations. Pages under 800 words averaged 3.2.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freshness (ChatGPT)&lt;/td&gt;
&lt;td&gt;Pages updated in the last three months averaged 6 citations. Older pages averaged 3.6.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Section size&lt;/td&gt;
&lt;td&gt;About 120 to 180 words between headings beat very short sections.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authority (ChatGPT)&lt;/td&gt;
&lt;td&gt;Sites with over 32,000 referring domains were 3.5 times more likely to be cited than sites with 200 or fewer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google overlap&lt;/td&gt;
&lt;td&gt;92.36% of AI Overviews linked to at least one domain already in Google's top 10.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://ahrefs.com/blog/answer-engine-optimization/" rel="noopener noreferrer"&gt;Ahrefs adds&lt;/a&gt; that 44.2% of citations came from the first 30% of a page.&lt;/p&gt;

&lt;p&gt;Google says the same thing in plain terms. Its &lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;guide to AI features in Search&lt;/a&gt; says there are no extra requirements to appear in AI Overviews or AI Mode, and no new machine readable files or special markup are needed. A page just has to be indexed and eligible to show with a snippet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to structure developer docs so AI assistants cite them: an 8 step playbook
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Let the right crawlers in
&lt;/h3&gt;

&lt;p&gt;You cannot write your way out of a page the crawler can't see. Start with access.&lt;/p&gt;

&lt;p&gt;Here is a simple robots.txt that allows the bots that fetch pages to answer questions, and leaves the choice on training bots to you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bots that fetch pages to answer live questions&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt;-agent: OAI-SearchBot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="nc"&gt;User&lt;/span&gt;-agent: ChatGPT-User
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="nc"&gt;User&lt;/span&gt;-agent: PerplexityBot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="nc"&gt;User&lt;/span&gt;-agent: Claude-SearchBot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="nc"&gt;User&lt;/span&gt;-agent: Googlebot
&lt;span class="nc"&gt;Allow&lt;/span&gt;: /

&lt;span class="c"&gt;# Bots that collect data for training (your call)&lt;/span&gt;
&lt;span class="c"&gt;# User-agent: GPTBot&lt;/span&gt;
&lt;span class="c"&gt;# Disallow: /&lt;/span&gt;
&lt;span class="c"&gt;# User-agent: ClaudeBot&lt;/span&gt;
&lt;span class="c"&gt;# Disallow: /&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four details trip up a lot of teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Render on the server.&lt;/strong&gt; &lt;a href="https://vercel.com/blog/the-rise-of-the-ai-crawler" rel="noopener noreferrer"&gt;Vercel and MERJ found&lt;/a&gt; that none of the major AI crawlers they measured ran JavaScript. Gemini is the exception because it uses Google's crawling setup. That study is from December 2024, so check your own logs, but build as if it still holds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your firewall and CDN.&lt;/strong&gt; Many sites block AI bots by accident with a security rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not hide public docs behind a login or cookie wall.&lt;/strong&gt; A crawler will not sign in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep code samples and tables in the HTML.&lt;/strong&gt; Do not load them with a script after the page opens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;a href="https://tryhoverify.com/blog/generative-engine-optimization-statistics/" rel="noopener noreferrer"&gt;September 2026 sample of 95 sites&lt;/a&gt; found training crawlers get blocked about twice as often as retrieval crawlers. If you want citations, block training bots by name if you wish, and keep the fetching bots allowed.&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%2F2j2icoy5ewq9mrt3p1lu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2j2icoy5ewq9mrt3p1lu.png" alt="img-2" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Put the answer in the first two sentences
&lt;/h3&gt;

&lt;p&gt;Give every page one question and answer it straight away. Name the page after the task, in the words a developer would type.&lt;/p&gt;

&lt;p&gt;Here is the difference.&lt;/p&gt;

&lt;p&gt;A weak opening: &lt;em&gt;Webhooks are a way for our platform to notify your application when events happen. Our system sends HTTP POST requests to a URL you configure. For security reasons, it is important to make sure requests are genuine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A strong one: &lt;em&gt;To verify a webhook, compute an HMAC SHA-256 hash of the raw request body with your signing secret, then compare it to the value in the X-Signature header. If they match, the request is genuine. The full code is below in Node.js, Python, and Go.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The strong version can be lifted out of the page and still be a correct answer. That is what an assistant looks for.&lt;/p&gt;

&lt;p&gt;Then write the page in this order: the answer, the working steps or code, the edge cases, and two or three links to the next likely questions. Title it "How to verify webhook signatures in Node.js," not "Webhooks."&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make every section stand on its own
&lt;/h3&gt;

&lt;p&gt;An assistant does not read your page top to bottom. It pulls out one section. If that section says "as mentioned above," it falls apart when copied.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name the thing.&lt;/strong&gt; Write "the Acme Payments API," not "it."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat key facts where they matter.&lt;/strong&gt; If a rate limit applies to three endpoints, say it on all three pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link for more, but do not link for meaning.&lt;/strong&gt; The section should make sense without a click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use question style headings.&lt;/strong&gt; "What happens when a request is rate limited?" matches how people prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not chop everything into tiny pieces. SE Ranking's data favored roughly 120 to 180 words between headings over very short fragments. Short paragraphs help readers, but a section still needs enough context to be understood alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Show code that works the first time
&lt;/h3&gt;

&lt;p&gt;A good code block is often the most citable part of a docs page. Make it complete, with imports, setup, and the call. No "..." in the middle.&lt;/p&gt;

&lt;p&gt;Then add four things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;language and the SDK or API version&lt;/strong&gt; it works with.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;expected output&lt;/strong&gt;, so the reader can confirm it worked.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;exact text of the common error&lt;/strong&gt;, right under the example. People paste error messages straight into assistants, and exact text is a strong match.&lt;/li&gt;
&lt;li&gt;The same example in &lt;strong&gt;each language your users use&lt;/strong&gt;, as separate labeled blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A table of error codes is one of the best things you can publish. It is dense, exact, and easy to quote.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Offer a clean text version of every page
&lt;/h3&gt;

&lt;p&gt;A page full of menus, banners and scripts is noisy. A plain text version is cheaper for an assistant to read and easier to quote correctly.&lt;/p&gt;

&lt;p&gt;Several developer companies already do this. Stripe lets you &lt;a href="https://ravichaganti.com/blog/documentation-stack-for-ai-agents/" rel="noopener noreferrer"&gt;add &lt;code&gt;.md&lt;/code&gt; to a docs URL&lt;/a&gt; to get a markdown version. &lt;a href="https://vercel.com/kb/guide/make-your-documentation-readable-by-ai-agents" rel="noopener noreferrer"&gt;Vercel's guide&lt;/a&gt; covers serving markdown through the &lt;code&gt;Accept: text/markdown&lt;/code&gt; header and a link tag in the page head. Fastly &lt;a href="https://www.fastly.com/documentation/reference/markdown/" rel="noopener noreferrer"&gt;serves markdown automatically&lt;/a&gt; to known AI user agents.&lt;/p&gt;

&lt;p&gt;Now the honest part about &lt;code&gt;llms.txt&lt;/code&gt;the proposed file that lists your key pages for AI tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For search citations, no evidence it helps.&lt;/strong&gt; SE Ranking checked about 300,000 domains. Only 10.13% had the file, and it found no link to citation counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Few files get read.&lt;/strong&gt; Ahrefs studied 137,000 domains and found that &lt;a href="https://ppc.land/llms-txt-adoption-rises-8-8x-but-97-of-files-get-zero-ai-requests/" rel="noopener noreferrer"&gt;28% published the file and 97% of those got zero requests in May 2026&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For coding assistants, it can help.&lt;/strong&gt; Mintlify ran &lt;a href="https://www.mintlify.com/blog/state-of-docs-traffic" rel="noopener noreferrer"&gt;2,400 test runs across 20 docs sites&lt;/a&gt; and found that one link to llms.txt removed most of the 404 errors agents hit while guessing page addresses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat llms.txt as a helper for coding tools, not a ranking trick. It costs almost nothing, so if your docs platform makes it for you, leave it on.&lt;/p&gt;

&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%2Fmj9xm57u8bwplf0zjojo.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%2Fmj9xm57u8bwplf0zjojo.webp" alt="benchmark" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Show versions and update dates
&lt;/h3&gt;

&lt;p&gt;Fresh pages get cited more. In SE Ranking's data, pages updated in the last three months averaged 6 ChatGPT citations against 3.6 for older ones. Ahrefs reports that AI-cited URLs are on average 25.7% fresher than normal search results.&lt;/p&gt;

&lt;p&gt;For docs, freshness is also about being right. An answer that uses a retired endpoint hurts the developer and then hurts you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show &lt;strong&gt;"Last updated"&lt;/strong&gt; and the &lt;strong&gt;API or SDK version&lt;/strong&gt; at the top of each page.&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;old version pages&lt;/strong&gt;, clearly marked, with a link to the current one.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;redirects&lt;/strong&gt; when a page moves.&lt;/li&gt;
&lt;li&gt;Publish a &lt;strong&gt;dated changelog&lt;/strong&gt;. It answers "what changed in version 3?"&lt;/li&gt;
&lt;li&gt;Review your &lt;strong&gt;top 20 pages&lt;/strong&gt; every quarter, and only change the date when you really changed the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Publish comparison and migration pages
&lt;/h3&gt;

&lt;p&gt;Developers ask assistants to compare options and plan moves. "Acme vs Provider X for webhooks." "How to migrate from Provider X to Acme."&lt;/p&gt;

&lt;p&gt;Write these pages in the open. State what each option does well, show real numbers, and say who each one fits. Pages that read as an honest guide get used. Pages that read as an ad get skipped.&lt;/p&gt;

&lt;p&gt;If you want the broader playbook for getting any developer product recommended, Studio1 covers it in &lt;a href="https://www.studio1hq.com/blog/how-to-get-your-tool-recommended-by-chatgpt-perplexity-google" rel="noopener noreferrer"&gt;How to Get Your Tool Recommended by ChatGPT, Perplexity, and Google&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Earn mentions outside your docs
&lt;/h3&gt;

&lt;p&gt;On page work gets you into the race. Authority decides who wins it. In SE Ranking's study, referring domains were the strongest signal for ChatGPT citations. For Google AI Mode, domains with over 24,000 referring domains averaged 6.8 citations against 2.5 for sites with fewer than 300.&lt;/p&gt;

&lt;p&gt;The same study found domains with a strong presence on Reddit and Quora had roughly four times the citations of domains with little. That is a correlation, since bigger brands get mentioned everywhere. Still, developers trade answers in communities, and assistants read those places too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Publish original data.&lt;/strong&gt; Benchmarks and usage numbers give others a reason to link to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer questions in public.&lt;/strong&gt; Stack Overflow, GitHub discussions and forums, with honest answers that link to the exact docs page when it helps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get listed where developers look.&lt;/strong&gt; Package registries, integration directories and "awesome" lists on GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make your docs easy to link to.&lt;/strong&gt; Stable URLs and heading anchors let people point at the exact section.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can templated pages help your docs get cited?
&lt;/h2&gt;

&lt;p&gt;Yes, if each page has real data behind it. Docs are well suited to programmatic SEO, which means building many pages from one template and a set of real facts. You probably have hundreds of items that each deserve a page.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page type&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Data you need&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Error code&lt;/td&gt;
&lt;td&gt;What does error 429 mean and how do I fix it?&lt;/td&gt;
&lt;td&gt;Exact message, causes, fix, working code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint or SDK method&lt;/td&gt;
&lt;td&gt;How do I call this and what comes back?&lt;/td&gt;
&lt;td&gt;Parameters, example request and response, limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;How do I use Acme with Next.js?&lt;/td&gt;
&lt;td&gt;Setup steps, tested code, known issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration&lt;/td&gt;
&lt;td&gt;How do I move from Provider X to Acme?&lt;/td&gt;
&lt;td&gt;Concept mapping, code before and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparison&lt;/td&gt;
&lt;td&gt;Acme vs Provider X for webhooks?&lt;/td&gt;
&lt;td&gt;Real limits, pricing, and honest trade offs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple rule: do not publish a templated page until you can answer four questions with facts unique to that page. What exactly goes wrong or is needed? Why does it happen? What is the working fix or example? Which real numbers apply?&lt;/p&gt;

&lt;p&gt;If a page would read the same with one word swapped, search engines and assistants will skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you know if AI assistants are citing your docs?
&lt;/h2&gt;

&lt;p&gt;Normal analytics miss most of this. Mintlify notes that coding agents &lt;a href="https://www.mintlify.com/library/how-to-track-ai-traffic-to-your-documentation" rel="noopener noreferrer"&gt;rarely show up in Google Analytics&lt;/a&gt; because they fetch pages without running the tracking script. Use three checks instead.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read your server logs.&lt;/strong&gt; Filter by user agent: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, Googlebot. You will see which pages they fetch and which they miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run a fixed prompt test.&lt;/strong&gt; Write 20 real questions your users ask. Ask each in ChatGPT, Gemini, Perplexity, Claude, and one coding assistant. Record whether your docs are cited, whether a competitor is, and whether the answer is correct. Repeat monthly with the same list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch search and referrals.&lt;/strong&gt; Search Console includes AI features in its reports. Track AI referral visits as their own group. SE Ranking found these visitors &lt;a href="https://visible.seranking.com/blog/answer-engine-optimization/" rel="noopener noreferrer"&gt;spent 68% more time on site&lt;/a&gt; than organic search visitors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the answers, not only the links. Being cited is not the same as being recommended.&lt;/p&gt;

&lt;h2&gt;
  
  
  What mistakes keep developer docs out of AI answers?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs that only render in the browser.&lt;/strong&gt; The crawler sees an empty page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A long warm up before the answer.&lt;/strong&gt; The useful part sits too far down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sections that depend on other sections.&lt;/strong&gt; They fall apart when quoted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code with gaps.&lt;/strong&gt; Snippets with "..." and no output cannot be trusted or copied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old versions with no label.&lt;/strong&gt; Assistants repeat outdated answers, and developers blame you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Betting on special files.&lt;/strong&gt; FAQ schema showed no lift in SE Ranking's data: pages with it averaged 3.6 citations, pages without averaged 4.2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thin templated pages.&lt;/strong&gt; Same text, one word changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No presence outside your own site.&lt;/strong&gt; Authority is still the biggest signal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does llms.txt help my docs get cited by ChatGPT or Google?
&lt;/h3&gt;

&lt;p&gt;Not according to current data. SE Ranking found no link between llms.txt and citation counts across about 300,000 domains, and Ahrefs found 97% of the files in its 137,000 domain study got no requests in May 2026. It may help coding assistants find your pages, so it is fine to publish. Just do not count on it for citations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need FAQ schema or special markup?
&lt;/h3&gt;

&lt;p&gt;No. Google says no special schema or markup is required for AI Overviews and AI Mode. A visible FAQ section can still help readers, and pages with FAQ content averaged slightly more citations in AI Mode (4.9 against 4.4), but the markup itself did not matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long should a developer docs page be?
&lt;/h3&gt;

&lt;p&gt;Long enough to fully answer the task. In SE Ranking's ChatGPT data, pages above 2,900 words averaged more citations than pages under 800 words. Do not pad to hit a number. Cover prerequisites, steps, code, output, errors, and limits, and stop there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will AI assistants read docs that rely on JavaScript?
&lt;/h3&gt;

&lt;p&gt;Often not. Vercel and MERJ found that major AI crawlers such as GPTBot and ClaudeBot did not run JavaScript in their study. Render your docs on the server so the full text is in the HTML.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should I update my docs?
&lt;/h3&gt;

&lt;p&gt;Review your most important pages at least every three months. Pages updated in that window averaged 6 ChatGPT citations in SE Ranking's data against 3.6 for older pages. Update when facts change, and show the date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is AEO different from SEO?
&lt;/h3&gt;

&lt;p&gt;It is the same foundation applied to a new place. Crawlability, authority, helpful content, and freshness matter for both. The change is writing so single passages can be quoted, and measuring citations and mentions alongside clicks.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>10 Projects You Should Build with Jev</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:25:08 +0000</pubDate>
      <link>https://dev.to/studio1hq/10-projects-you-should-build-with-jev-44l1</link>
      <guid>https://dev.to/studio1hq/10-projects-you-should-build-with-jev-44l1</guid>
      <description>&lt;p&gt;JEV is getting a lot of attention right now. &lt;/p&gt;

&lt;p&gt;People are excited about it, and developers are already experimenting with ways to use it inside real applications.But there is a common mistake when trying a new AI model like JEV: treating it like another general-purpose LLM.&lt;/p&gt;

&lt;p&gt;JEV becomes much more interesting when you use it for decisions rather than generation. Instead of asking it to write an email or generate a long answer, you give it some context, define the decisions it can make, and let your application act on the result.For example, a support system could use JEV to decide whether a ticket belongs to billing, engineering, or sales. A browser agent could use it to choose which button to click next. An AI application could use it to decide which model should handle a particular request.The model makes the decision, while your code handles the action.That simple pattern opens up a lot of interesting use cases. Here are 10 projects worth building with JEV.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Build a smart support ticket router
&lt;/h2&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%2Fg98gtenx38hg1nvhfq13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg98gtenx38hg1nvhfq13.png" alt="first-usecase" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Support teams spend a lot of time reading incoming tickets and deciding where each one should go. A ticket might be a billing question, a technical issue, a feature request, or an urgent complaint from an unhappy customer.JEV can handle the first layer of that workflow.You can give it the ticket and ask which team should handle it, how urgent the issue is, whether the customer appears frustrated, and whether the ticket needs immediate human attention. Your application can then use those decisions to route the ticket automatically.A technical issue can go to engineering, a refund request can go to billing, and an urgent issue can be moved to the top of the queue.The important part is that JEV does not need to write the customer response. A generative model can handle that later if needed. JEV only needs to make the decision that determines what happens next.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Build an inbound lead scorer
&lt;/h2&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%2Ffv7y8otb52w82rqc2h90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv7y8otb52w82rqc2h90.png" alt="second-usecase" width="799" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same approach works well for sales.A typical website can receive a mix of serious prospects, students, competitors, and people who are simply exploring a product. Sales teams often have to spend time sorting through these submissions before deciding which ones deserve attention.You can use JEV to score each lead based on information such as the person's job title, company, form submission, and other available context.The model can determine whether the lead fits your ideal customer profile, whether they appear ready to buy, whether they are likely to be a decision-maker, and how the lead should be prioritized.Your application can then route high-priority leads directly to sales while sending lower-priority leads into other workflows.This turns JEV into a lightweight decision layer for your sales pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Build a comment moderation system
&lt;/h2&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%2Fsiz3otcgk42levm7fq1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsiz3otcgk42levm7fq1i.png" alt="third-usecase" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moderation is another problem that can be expressed as a set of decisions.For a community, social platform, or Discord server, you can give JEV a comment and ask whether it is spam, harassment, unsafe content, or something that requires human review.Your application can then use those results to decide what happens to the comment. Clearly safe content can be published automatically, while potentially harmful or uncertain content can be sent to a moderator.You can also introduce confidence thresholds so that the system only automates decisions when JEV is sufficiently confident.This creates a useful balance between automation and human review instead of assuming that every AI decision should be acted on automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Build an AI citation checker
&lt;/h2&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%2Flana5yuoc08021nsxj0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flana5yuoc08021nsxj0m.png" alt="fourth-usecase" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI-generated content often includes claims that sound convincing but are not actually supported by their sources.A simple JEV application could check whether a specific source supports a specific claim.Give the model the claim and the relevant source, then ask whether the source supports the claim, contradicts it, or does not contain enough information to determine the answer.For example, if an AI-generated report says that a company grew its revenue by 40% while the cited source says it grew by 12%, the application can flag the mismatch before the report reaches the user.This could be useful for research tools, education platforms, content workflows, and AI agents that work with external sources.The key is to keep the question narrow. You are not asking JEV to research the entire internet. You are asking it to make one specific judgment about a piece of evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Build a smarter RAG context picker
&lt;/h2&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%2Fxuzbxohllltiat8oostz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxuzbxohllltiat8oostz.png" alt="fifth-usecase" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RAG systems often retrieve several documents and pass them directly into the context of a larger language model. The problem is that not every retrieved document is useful.Some documents may be irrelevant, outdated, contradictory, or contain instructions that should not be passed into the final context.JEV can sit between retrieval and generation as a filtering layer.For example, your search system could retrieve 20 documents and JEV could decide which ones are relevant to the user's question, which ones are outdated, whether any documents conflict with each other, and whether a document should be included in the final context.The main LLM can then focus on generating the answer using a cleaner set of information.In this architecture, the LLM handles generation while JEV helps decide what information the LLM should see.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Build a semantic code review bot
&lt;/h2&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%2Fln2gaqdjhci9waia14oz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fln2gaqdjhci9waia14oz.png" alt="sixth-usecase" width="799" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traditional linters are good at catching problems that can be expressed as explicit rules, such as formatting issues, type errors, and missing imports.Engineering teams also have rules that are much harder to express through traditional static analysis.For example, does a new API expose private customer information? Does a pull request bypass an important approval step? Does a change introduce a risky pattern? Did the developer add tests for the new behavior?These are questions that can be handled by a decision model.You can give JEV the changed code along with the relevant project rules and ask it to evaluate a small set of specific conditions. When it identifies a likely problem with high confidence, your GitHub workflow can flag the pull request or request human review.This does not replace human code review. Instead, it adds another layer that can catch potential issues before an engineer spends time reviewing the entire change.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Build an AI model router
&lt;/h2&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%2Fznsno8vhpwka0bnj51is.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fznsno8vhpwka0bnj51is.png" alt="seventh-usecase" width="799" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modern AI applications often have access to several models, and sending every request to the most expensive model is rarely necessary.A simple question might be handled by a small model, while a complex coding task could require a more capable model. Some requests may need retrieval, a tool, a business workflow, or even a human instead of another model.\&lt;br&gt;
\&lt;br&gt;
Give it the user's request and ask it to classify the task based on the characteristics that matter to your application. Your code can then choose the appropriate model or workflow.A simple FAQ can go to a cheaper &amp;amp; faster model like &lt;a href="https://tokenfactory.nebius.com/?modals=endpoint-details&amp;amp;model-id=zai-org/GLM-5.3-Flash" rel="noopener noreferrer"&gt;GLM 5.3 Flash&lt;/a&gt; through &lt;a href="https://x.com/@nebiustf" rel="noopener noreferrer"&gt;@nebiustf&lt;/a&gt; , a difficult coding task can go to a stronger model like &lt;a href="https://tokenfactory.nebius.com/?modals=endpoint-details&amp;amp;model-id=zai-org/GLM-5.3" rel="noopener noreferrer"&gt;GLM 5.3&lt;/a&gt;, and a refund request can be handled by your existing business logic.JEV does not need to answer the user's question. It only needs to decide where that question should go.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Build an AI agent safety firewall
&lt;/h2&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%2Ffnsjkmv270znbgpkguhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnsjkmv270znbgpkguhd.png" alt="eight-usecase" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI agents can now browse websites, send emails, modify records, call APIs, and perform other actions on behalf of users. That makes it important to evaluate an action before allowing an agent to execute it.Imagine an agent wants to delete hundreds of customer records. Before executing the action, you could use JEV to evaluate whether the action is irreversible, whether it involves sensitive information, whether it matches the user's request, and whether it should be allowed, confirmed by the user, or blocked.Your application can then enforce that decision.Low-risk actions can proceed automatically, medium-risk actions can require confirmation, and high-risk actions can be blocked.JEV should not be treated as a replacement for permissions, access controls, validation, or other security measures. It can instead provide an additional semantic layer between an AI agent and the actions it wants to perform.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Build a browser agent reflex layer
&lt;/h2&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%2Fu50j0dpkw1avgw80hawp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu50j0dpkw1avgw80hawp.png" alt="ninth-usecase" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browser agents spend much of their time making small decisions. They need to decide which button to click, which search result to open, whether to scroll, whether to go back, or which tab to select.These decisions do not always require a large generative model.You can give JEV a compact representation of the current browser state along with a limited set of possible actions. It can then choose the action that best moves the agent toward its goal.Your browser automation system executes that action, the page changes, and JEV makes the next decision.A larger model can still handle tasks that require generation or more complex planning, such as writing an email or filling out a long form.This creates a clean architecture where a larger model handles planning, JEV handles fast decisions, and your automation code handles execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Build a real-time game AI
&lt;/h2&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%2Flzts8xb6oyoftw84fwvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzts8xb6oyoftw84fwvs.png" alt="tenth-usecase" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most interesting ways to experiment with JEV is to use it as the decision-maker for a small game.Instead of giving the model a huge screenshot and asking it to play the entire game, represent the game state in a compact format. You might tell it that an enemy is nearby, the player's health is low, an obstacle is ahead, and the available actions are attack, jump, retreat, heal, or block. JEV chooses an action, the game executes it, and the updated state is sent back for the next decision.This creates a simple loop where the application provides the state, JEV selects an action, and the game executes it.The same architecture can work for a platformer, driving game, tower defense game, or almost any environment with a relatively small action space.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger idea behind JEV
&lt;/h2&gt;

&lt;p&gt;Although these projects look very different, they all follow the same basic architecture. Your application has some state that needs to be interpreted. JEV makes a small, specific decision about that state, and your code takes the resulting action.That makes JEV less interesting as a replacement for a general-purpose chatbot and more interesting as a decision layer inside software.The best use cases are often the decisions that happen repeatedly throughout a product: which ticket should be escalated, which model should handle a request, which document belongs in the context, whether an agent action is safe, or which browser action should happen next.Once you start looking at software through that lens, you can find many places where a fast decision model can fit.So instead of building another chatbot with JEV, look for a workflow where information comes in, your application needs to make a decision, and that decision triggers an action.Let JEV make the decision, and let your code handle everything that happens after it.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Gemini 4 Argon: What's New, What It Costs, and Whether to Switch</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:13:43 +0000</pubDate>
      <link>https://dev.to/studio1hq/gemini-4-argon-whats-new-what-it-costs-and-whether-to-switch-1m7o</link>
      <guid>https://dev.to/studio1hq/gemini-4-argon-whats-new-what-it-costs-and-whether-to-switch-1m7o</guid>
      <description>&lt;p&gt;Google DeepMind recently launched Gemini 4 Argon, with a big benchmark table and a small guest list. Google's own numbers have it winning or tying 13 of 18 tests against Opus 5.5, Fable 5.1 and GPT-6 Astra. But for now it's only open to a group of cyber defenders.&lt;/p&gt;

&lt;p&gt;So the useful question isn't whether it's the best model. It's what you should check before you switch, and what it'll cost you when you do. Here's what we know so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Argon wins or ties 13 of 18 benchmarks in Google's table, mostly knowledge work, long context, and DeepSWE. It loses on FrontierSWE and Terminal-bench.&lt;/li&gt;
&lt;li&gt;The output limit jumps from 64K to 1M tokens. It also writes a lot: 62k tokens per task against 27k for GPT-6 Astra in one independent test.&lt;/li&gt;
&lt;li&gt;Launch price is $2 per million input tokens and $10 per million output. It doubles to $4 and $20 later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Gemini 4 Argon?
&lt;/h2&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%2Fyq1byreamlb0ie8odxoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyq1byreamlb0ie8odxoe.png" alt="logo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Argon is Google's new flagship model. It's the first Gemini above the Flash tier in more than seven months, and it quietly replaces the Gemini 3.5 Pro that Google teased at I/O in May and never shipped.&lt;/p&gt;

&lt;p&gt;Google built it for long, multi-step work: real-world software engineering, enterprise knowledge work like legal and finance, and cyber defense. Two details stand out for developers. The output limit is now 1M tokens, up from 64K. And it was trained to find, validate and patch software vulnerabilities on its own.&lt;/p&gt;

&lt;p&gt;Then there's access. Argon went to trusted cyber defenders in Google's Fairwind Program on September 30. Google says paid API customers and Google AI Ultra subscribers come next, then developers, enterprises and consumers, once the guardrails have been tuned with early testers. It also says it's taking part in the US government's voluntary pre-release access process while it widens the rollout.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it differs from the other models
&lt;/h2&gt;

&lt;p&gt;As compared to the previous versions, the big change is the 1M-token output limit and the cyber training. Google says Argon makes clear leaps over Gemini 3.8 Flash Cyber, its previous security-focused model.&lt;/p&gt;

&lt;p&gt;Against the rest of the field, the pattern in Google's own table is easy to read. Argon leads on knowledge work, long context, and DeepSWE. The other models keep the shell-and-terminal benchmarks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Leads on (in Google's table)&lt;/th&gt;
&lt;th&gt;Reading&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 4 Argon&lt;/td&gt;
&lt;td&gt;Vals Index, AutomationBench, Vals Finance Agent v2, Harvey's Legal Agent, DeepSWE, Vibe Code Bench, RiemannBench, both GraphWalks tests, Agent's Last Exam, Chartography, LVBench&lt;/td&gt;
&lt;td&gt;Best on document-heavy and long-context work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra&lt;/td&gt;
&lt;td&gt;FrontierSWE v2, Terminal-Bench Science 0.1, OSWorld-2.0&lt;/td&gt;
&lt;td&gt;Best on harder agentic and computer-use tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5.5&lt;/td&gt;
&lt;td&gt;Terminal-bench 4.0, PostTrainBench&lt;/td&gt;
&lt;td&gt;Best on shell agents and ML engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Fable 5.1&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Never first in the table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On cybersecurity, Argon and GPT-6 Astra tie at 68% on CWE-bench v1.&lt;/p&gt;

&lt;p&gt;For a neutral check, Artificial Analysis says Argon matches GPT-6 Astra on its Intelligence Index and sits one point ahead of GPT-6.1 Sol.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test scores
&lt;/h2&gt;

&lt;p&gt;Treat these as best cases. They're Google's chosen benchmarks, mostly from its own runs, as tabulated by &lt;a href="https://thenewstack.io/google-gemini-4-argon/" rel="noopener noreferrer"&gt;The New Stack&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%2F7adw8pm54tt9oikcxvht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7adw8pm54tt9oikcxvht.png" alt="table-of-content" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three caveats on reading it.&lt;/p&gt;

&lt;p&gt;First, several leads are narrow. On the Vals Index and Vibe Code Bench, the gap is under two points. Vibe Code Bench is a tie in practice, since all four models score above 89%.&lt;/p&gt;

&lt;p&gt;Second, the harness matters. On CWE-bench v1, the OpenAI and Anthropic models ran in their own agent harnesses, Codex and Claude Code, so that score reflects model plus tooling. And &lt;a href="https://artificialanalysis.ai/articles/gemini-4-argon-google-top-three-labs" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt; ran Terminal Bench 4 separately and put Argon at 57%, behind Claude Sonnet 5.5 (64%), Opus 5.5 (60%), and GPT-6 Astra (59%).&lt;/p&gt;

&lt;p&gt;Third, the biggest gaps are in knowledge work and long context. The legal result looks huge next to Fable 5.1, but 19.6% still means about one task in five completed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it will really cost you
&lt;/h2&gt;

&lt;p&gt;Price per token is the wrong number to compare. Artificial Analysis measures cost per task, and that's where Argon's chattiness shows up.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Cost per Intelligence Index task&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 4 Argon, launch price ($2 in, $10 out)&lt;/td&gt;
&lt;td&gt;$1.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 4 Argon, full price ($4 in, $20 out), my estimate&lt;/td&gt;
&lt;td&gt;about $3.98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra, max effort&lt;/td&gt;
&lt;td&gt;$3.26&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first and last rows are Artificial Analysis's figures. The middle row is mine. The launch discount is a 50% promotion, so if Argon keeps using the same number of tokens, doubling the price doubles the cost. Astra's price could change too, so treat that row as a rough guide, not a forecast.&lt;/p&gt;

&lt;p&gt;A few other anchors. Opus 5.5 charges $20 per million output tokens, the same as Argon's post-launch rate. GPT-6.1 Sol's newly discounted price matches Argon's launch price. One maxed-out 1M-token response costs $10 now and $20 later. Cached input tokens get 95% off the input price, which should help if you resend the same repo-sized prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check before you switch
&lt;/h2&gt;

&lt;p&gt;I didn't find any migration notes in Google's announcement or the coverage, so this isn't a changelog of breaking changes. It's a checklist of what the launch details imply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You can't switch yet.&lt;/strong&gt; Access is gated, and the model ships without cyber guardrails only for Fairwind participants and Google's own teams. Google is still tuning the safeguards before a wider release, so security-heavy prompts may behave differently once you get access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisit your output settings.&lt;/strong&gt; The limit went from 64K to 1M tokens. If you cap max output, set timeouts, or stream into something with size limits, those choices were made for 64K. Raise the ceiling on purpose, not by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expect a chattier model.&lt;/strong&gt; Artificial Analysis measured 62 K output tokens per task for Argon against 27 K for GPT-6 Astra at max effort. Watch latency and bills, not just quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for the price change.&lt;/strong&gt; $2 and $10 per million tokens is an introductory rate. It becomes $4 and $20.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Google has built with it
&lt;/h2&gt;

&lt;p&gt;Everything below comes from &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-4-argon/" rel="noopener noreferrer"&gt;Google's own announcement&lt;/a&gt; and describes internal use. Read it as a demo reel, not a benchmark.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C/C++ to Rust migrations.&lt;/strong&gt; Argon agents are porting code across Google, from tens of thousands of lines in libraries like re2 and libgav1 up to 800K+ lines for the Fuchsia Zircon kernel. Google says these rewrites are still going through automated and manual audits, emulation testing, and review before production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;libgav1.&lt;/strong&gt; Starting from an existing Rust port, agents replaced 32K lines of SIMD code with safe Rust that the compiler vectorizes on its own. Google reports a memory-safe video decoder that runs 2.7x faster than the port, with identical video output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data center memory.&lt;/strong&gt; A team of agents analyzed fleet-wide profiling data and applied memory optimizations, freeing over 300 TiB once rolled out. Google estimates 500 TiB to 1 PiB in total.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quantum.&lt;/strong&gt; Argon beat a published baseline by 40% on the qubits-times-gates cost of one subroutine, in a matter of minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security.&lt;/strong&gt; Wiz, which Google acquired in March, is using Argon in its Scan for Good program. Google says it found a critical flaw in healthcare software used by hospitals worldwide that earlier frontier models missed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The libgav1 story is the one worth stealing. The loop is profile, run an experiment, read what the compiler produced, repeat for many rounds. You can run that loop with any capable model today. Google's claim is that Argon stays on it without losing the thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it still loses
&lt;/h2&gt;

&lt;p&gt;This is the part the launch post skips.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FrontierSWE v2.&lt;/strong&gt; Argon scores 55.0% to GPT-6 Astra's 65.5%, and it's last of the four models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal-bench 4.0.&lt;/strong&gt; Opus 5.5 leads with 66.4% against Argon's 57.4%, a nine-point gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other agentic and science tests.&lt;/strong&gt; Astra beats it on Terminal-Bench Science 0.1 (68.1% to 57.6%) and OSWorld-2.0 (72.6% to 69.2%). Opus 5.5 beats it on PostTrainBench (49.3% to 45.3%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cyber comparisons.&lt;/strong&gt; Google compares its security results only with its own Gemini 3.8 Flash Cyber: 85.8% against 71.0% on its internal vulnerability benchmark, and 70.9% against 58.2% on Wiz's penetration testing benchmark. That shows progress, not rank.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independent testing.&lt;/strong&gt; Very few people outside Google have had hands-on time. Bloomberg reported before launch that some people inside Google worry Argon isn't as strong as the Anthropic and OpenAI models. Google disputes that and says employees have been testing versions for weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost per task.&lt;/strong&gt; Once the launch discount ends, Argon may not be the cheap option (see above).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more loose end. Google's post doesn't state a context window, and one outlet reports 2M tokens. I'd wait for the docs.&lt;/p&gt;

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

&lt;p&gt;Argon looks strongest where the work is long and document-heavy: finance, legal, automation, big inputs, big outputs. It looks weaker in shell-driven agent work, where GPT-6 Astra and Opus 5.5 still hold the lead in Google's own table.&lt;/p&gt;

&lt;p&gt;The public benchmarks also disagree with each other. DeepSWE says one thing, FrontierSWE and Terminal-bench say another. So don't switch on a headline number. Pull 20 or so real tickets from your own repo, run them through whatever you use today, and run them through Argon once you have access.&lt;/p&gt;

&lt;p&gt;When you do, log two things: cost per task and whether a long output is still coherent at the end. Neither has been independently checked yet.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>google</category>
      <category>llm</category>
    </item>
    <item>
      <title>How to Get Your Tool Recommended by ChatGPT, Perplexity, and Google</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:10:55 +0000</pubDate>
      <link>https://dev.to/studio1hq/how-to-get-your-tool-recommended-by-chatgpt-perplexity-and-google-2dg9</link>
      <guid>https://dev.to/studio1hq/how-to-get-your-tool-recommended-by-chatgpt-perplexity-and-google-2dg9</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Getting your tool recommended by AI search isn't just about traditional SEO. You need to build the signals that ChatGPT, Perplexity, and Google use to understand, trust, and surface your product.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI recommendations depend on more than keywords. Clear positioning, authoritative content, technical depth, and consistent mentions all matter.&lt;/li&gt;
&lt;li&gt;Your documentation and technical content need to answer the exact questions developers are asking before they ever reach your website.&lt;/li&gt;
&lt;li&gt;Being mentioned across trusted websites, communities, GitHub, Reddit, and developer publications can strengthen your product's discoverability.&lt;/li&gt;
&lt;li&gt;The goal isn't to game AI search. It's to make your tool genuinely useful, easy to understand, and easy for AI systems to discover and cite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of developers nowadays ask ChatGPT before they ask Google. In &lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;&lt;strong&gt;Stack Overflow's 2025 survey&lt;/strong&gt;&lt;/a&gt;, 47.1% said they use AI tools every day, and another 17.7% use them every week. That adds up to about 65%. And when someone asks which tool to use, they don't get ten blue links. They get a short list of names, with a line or two on each.&lt;/p&gt;

&lt;p&gt;So the question for any developer tool is how to land on that list. Ranking well on Google used to be most of the answer. It covers less now: &lt;a href="https://ahrefs.com/blog/ai-overview-citations-top-10/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs found&lt;/strong&gt;&lt;/a&gt; that 76% of AI Overview citations came from pages in Google's top 10 in July 2025, and by January 2026 only 38% did. &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Google says&lt;/strong&gt;&lt;/a&gt; this is still SEO at heart. But studies of what AI tools cite, like &lt;a href="https://searchengineland.com/ai-search-engines-cite-reddit-youtube-and-linkedin-most-study-473138" rel="noopener noreferrer"&gt;&lt;strong&gt;this one from Peec AI&lt;/strong&gt;&lt;/a&gt;, show that being talked about on Reddit, YouTube, and review sites matters too. This guide covers both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is answer engine optimization (AEO)?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Answer engine optimization, or AEO, is the work of making your product easy for AI tools to find, understand, and name when someone asks a question. You will also see it called GEO (generative engine optimization) or LLM SEO. These all mean roughly the same thing.&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%2Fhmmjpzt63mflc509d5pu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhmmjpzt63mflc509d5pu.png" alt="answer-by-llm" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is different from classic SEO. SEO tries to get your link ranked on a results page. AEO tries to get your product's name inside the answer itself.&lt;/p&gt;

&lt;p&gt;Google sees it a little differently. Its own &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;guide to generative AI search&lt;/strong&gt;&lt;/a&gt; says that optimizing for these features is still SEO, because AI Overviews and AI Mode are built on its normal ranking and quality systems. When a question comes in, those features can run several related searches at once, which Google calls query fan-out, and then pull from the pages they find.&lt;/p&gt;

&lt;p&gt;Two things follow from that. First, basic SEO still matters, because the AI has to find your page before it can use it. Second, ranking is not the whole story. In an &lt;a href="https://ahrefs.com/blog/chatgpts-most-cited-pages/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs study of the 1,000 pages ChatGPT cited most in September 2025&lt;/strong&gt;&lt;/a&gt;, 28% had no organic search keywords at all. A page can be quoted by an AI tool without ranking anywhere in Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How do ChatGPT, Perplexity, and Google AI Overviews choose what to recommend?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Each one finds pages in its own way, so your first move is a little different for each.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Engine&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;How it finds your pages&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What studies say it leans on&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Your first move&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;Its own search crawler, OAI-SearchBot&lt;/td&gt;
&lt;td&gt;Wikipedia, Reddit, and editorial sites like Forbes&lt;/td&gt;
&lt;td&gt;Allow OAI-SearchBot, and make your homepage say plainly what the product does&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perplexity&lt;/td&gt;
&lt;td&gt;PerplexityBot, plus Perplexity-User when a person asks a question&lt;/td&gt;
&lt;td&gt;Reddit, LinkedIn, and G2 for business software questions&lt;/td&gt;
&lt;td&gt;Allow PerplexityBot, and earn real reviews and discussions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google AI Overviews and AI Mode&lt;/td&gt;
&lt;td&gt;Google's normal search index&lt;/td&gt;
&lt;td&gt;Pages that are indexed and can show a snippet. Google lists no extra requirements&lt;/td&gt;
&lt;td&gt;Get basic SEO right, and watch the AI report in Search Console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources for the table: &lt;a href="https://developers.openai.com/api/docs/bots" rel="noopener noreferrer"&gt;&lt;strong&gt;OpenAI's crawler page&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://docs.perplexity.ai/docs/resources/perplexity-crawlers" rel="noopener noreferrer"&gt;&lt;strong&gt;Perplexity's crawler page&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;&lt;strong&gt;Google's AI features page&lt;/strong&gt;&lt;/a&gt;, and the &lt;a href="https://searchengineland.com/ai-search-engines-cite-reddit-youtube-and-linkedin-most-study-473138" rel="noopener noreferrer"&gt;&lt;strong&gt;Peec AI study of 30 million cited sources&lt;/strong&gt;&lt;/a&gt; as reported by Search Engine Land.&lt;/p&gt;

&lt;p&gt;Two patterns show up across the studies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community sites carry a lot of weight.&lt;/strong&gt; Peec AI's analysis found Reddit was the most cited domain across ChatGPT, Google AI Mode, Gemini, Perplexity, and AI Overviews. YouTube, LinkedIn, Wikipedia, and Forbes also made the top five. Review sites like G2 and Yelp appeared often when people asked for recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most of what ChatGPT cites is not yours to edit.&lt;/strong&gt; In Ahrefs look at ChatGPT's top 1,000 cited pages, Wikipedia made up 29.7%, homepages and landing pages 23.8%, and how-to and explainer pages 19.4%. Ahrefs counted only 32.3% as pages a company could realistically get into: explainers, reviews, news, and blog posts. Ahrefs says its sorting of page types is a rough guide, so treat the exact split loosely. The useful part is the homepage number. You cannot pitch your way onto someone else's homepage, but your own is a page you fully control.&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%2Fsvz060s7j6iehe76w2zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsvz060s7j6iehe76w2zc.png" alt="Stats" width="701" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to get your product recommended by AI: an 8 step playbook for developers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Work through these in order. The first four are about your own site. The next four are about the rest of the web.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Let the right crawlers in&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If a crawler cannot read your site, nothing else in this guide matters. OpenAI says that to appear in ChatGPT search results, you should &lt;a href="https://help.openai.com/en/articles/12627856-publishers-and-developers-faq" rel="noopener noreferrer"&gt;&lt;strong&gt;allow OAI-SearchBot&lt;/strong&gt;&lt;/a&gt;. Perplexity says the same about &lt;a href="https://docs.perplexity.ai/docs/resources/perplexity-crawlers" rel="noopener noreferrer"&gt;&lt;strong&gt;PerplexityBot&lt;/strong&gt;&lt;/a&gt;. Google's &lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;&lt;strong&gt;AI features page&lt;/strong&gt;&lt;/a&gt; says crawling has to be allowed in robots.txt and by any CDN or hosting setup you use.&lt;/p&gt;

&lt;p&gt;A simple robots.txt that allows all three looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAI&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;SearchBot&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PerplexityBot&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Googlebot&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four details trip up a lot of teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPTBot is a separate bot. OpenAI treats it as its own setting, used for training. You can block GPTBot and still allow OAI-SearchBot, so blocking one does not have to hide you from ChatGPT search.&lt;/li&gt;
&lt;li&gt;Your firewall can block them even when robots.txt says yes. Perplexity asks you to allow its published IP ranges and walks through the setup for Cloudflare and AWS.&lt;/li&gt;
&lt;li&gt;Changes are not instant. Perplexity says it can take up to 24 hours for a robots.txt change to show up.&lt;/li&gt;
&lt;li&gt;Docs that only appear after JavaScript runs, or that live inside images or videos, are easy to miss. Google says important content should be available as text. A quick test: open your docs with JavaScript turned off and see what is left.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Put the answer in the first few lines&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Start every page with a plain answer to the question it exists for. Search Engine Land &lt;a href="https://searchengineland.com/chatgpt-citations-content-study-469483" rel="noopener noreferrer"&gt;&lt;strong&gt;reported a study&lt;/strong&gt;&lt;/a&gt; finding that 44% of ChatGPT citations come from the first third of a page's content. If your best sentence is in paragraph six, it may never get used.&lt;/p&gt;

&lt;p&gt;Here is the difference. A weak opening spends three paragraphs on how fast software is changing. A strong one looks like this (ExampleDB is made up for this guide):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;ExampleDB is a hosted Postgres database for small teams. It costs $15 a month, works with Prisma and Drizzle, and takes about five minutes to set up.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then write your headings as the questions people really type. "How do I connect ExampleDB to Next.js?" beats "Connecting". Keep paragraphs short, and put the answer first before the explanation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Publish the comparison pages people ask for&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Recommendation questions are comparison questions: "best tool for X", "X vs Y", "alternatives to Y". If you do not have a page for those, someone else writes it, and AI tools quote them instead.&lt;/p&gt;

&lt;p&gt;Write these pages yourself, and write them fairly. Put pricing in a table. Name the limits. Say who each tool suits, and where the other tool wins. A fair page is more useful to readers and easier to quote. If you run benchmarks, say how: the machine, the version, the date, and the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Google's guide to generative AI search&lt;/strong&gt;&lt;/a&gt; describes the content that holds up best as non-commodity: material built on first-hand experience that goes beyond what anyone could copy from other pages. Your own tests and your own numbers are exactly that.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Make your docs the best answer on the web&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For developer tools, docs do the job that blog posts do for other products. Developers ask "how do I add X to Next.js" or paste an error message straight into a chat. Cover those questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A quickstart that works when copied, with version numbers.&lt;/li&gt;
&lt;li&gt;One page per common error, with the exact error text in the heading, the cause, and the fix.&lt;/li&gt;
&lt;li&gt;Migration guides from the tools people are leaving for yours.&lt;/li&gt;
&lt;li&gt;Pricing, limits, and supported languages written out as text, not tucked into an image or a pricing widget.&lt;/li&gt;
&lt;li&gt;Everything public. Docs behind a login cannot be read by a crawler.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Get talked about where AI tools already look&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is the step most teams skip, and the data says it matters most. &lt;a href="https://ahrefs.com/blog/ai-overview-brand-correlation/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs studied 75,000 brands&lt;/strong&gt;&lt;/a&gt; and found that how often a brand is mentioned across the web had the strongest link to showing up in Google's AI Overviews. It scored 0.664, on a scale where 1 would be a perfect match. Backlinks scored 0.218. Brands in the top quarter for web mentions averaged 169 AI Overview mentions, against 14 for the next quarter down. Brands in the bottom half barely appeared at all.&lt;/p&gt;

&lt;p&gt;Two cautions. This is correlation, and Ahrefs says so itself. It also covered established brands (domain rating above 40), so for a new tool it shows direction, not a target.&lt;/p&gt;

&lt;p&gt;What to do about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer questions on Reddit and Stack Overflow when your tool is a fair answer. Say that you work on it.&lt;/li&gt;
&lt;li&gt;Make your GitHub README clear enough to quote: what it is, the install command, and a minimal example.&lt;/li&gt;
&lt;li&gt;Record tutorial videos with real transcripts. YouTube was in the top five most cited domains in the Peec AI data.&lt;/li&gt;
&lt;li&gt;Get into roundups, newsletters, and community lists that cover your category.&lt;/li&gt;
&lt;li&gt;Ask happy users for honest reviews on sites like G2, which showed up often for recommendation questions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not fake any of it. &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Google's guide&lt;/strong&gt;&lt;/a&gt; says chasing inauthentic mentions is not as helpful as it looks, and that its systems are built to catch spam.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;6. Say the same thing everywhere&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use the same product name, one-line description, price, and supported languages on your site, your GitHub page, your package pages (npm, PyPI, and so on), LinkedIn, and your docs. This one comes from common sense, not a study. If an AI tool reads three different prices, it may quote the wrong one or skip you.&lt;/p&gt;

&lt;p&gt;A short facts page helps: name, what it does, pricing, languages, license, and a last updated date. Every other page can then match it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;7. Keep key pages fresh, and be honest about dates&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AI tools lean toward newer pages. In the same &lt;a href="https://ahrefs.com/blog/chatgpts-most-cited-pages/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs look at ChatGPT's top 1,000 cited pages&lt;/strong&gt;&lt;/a&gt;, Ahrefs could find an update date for just over half. About 90% of those had been updated in 2025, and with Wikipedia removed, it was still about 82%. The sample is small, but the direction is clear.&lt;/p&gt;

&lt;p&gt;So update pricing, version numbers, and screenshots when they change, and show a real last updated date. Change that date only when you changechanged the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;8. Schema markup and llms.txt: nice to have, not a fix&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Google says&lt;/strong&gt;&lt;/a&gt; there is no special markup you need for AI Overviews, and that structured data should match the text people can see on the page. If your site setup makes FAQ or software markup easy, add it. Do not spend a sprint on it.&lt;/p&gt;

&lt;p&gt;The same goes for llms.txt. Google says its search &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;ignores those files&lt;/strong&gt;&lt;/a&gt;, so they neither help nor hurt you there. It also says it is fine to keep one for other services that use them. Treat it as optional, not a fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Can programmatic pages help your product get recommended by AI?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Yes, but only when every page carries real information. Programmatic SEO means building many pages from one template and a set of data. For developer tools that can be a very good fit, because so many developer questions follow a pattern: "X with Next.js", "X vs Y", "error 502 in X".&lt;/p&gt;

&lt;p&gt;The risk is real, though. Google's guide warns that making separate pages for every variation of a question, mainly to manipulate rankings or AI answers, &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;violates its scaled content abuse policy&lt;/strong&gt;&lt;/a&gt;. It also says a high number of pages does not make a site better. A template with the product name swapped in is the exact thing it describes.&lt;/p&gt;

&lt;p&gt;Here are page types that work, and the real data each one needs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Page type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Question it answers&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Real data every page needs&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Integration pages ("X with Next.js")&lt;/td&gt;
&lt;td&gt;How do I use X with this tool?&lt;/td&gt;
&lt;td&gt;Code you have tested, version numbers, known issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparison pages ("X vs Y")&lt;/td&gt;
&lt;td&gt;Which one should I pick?&lt;/td&gt;
&lt;td&gt;Pricing, limits, benchmarks with the method shown, where the other tool wins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error pages&lt;/td&gt;
&lt;td&gt;What does this error mean in X?&lt;/td&gt;
&lt;td&gt;The exact error text, the cause, a fix that works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case pages ("X for job queues")&lt;/td&gt;
&lt;td&gt;Is X good for my situation?&lt;/td&gt;
&lt;td&gt;A real example project or customer story&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language pages ("X in Go")&lt;/td&gt;
&lt;td&gt;How do I use X in my language?&lt;/td&gt;
&lt;td&gt;SDK version, a working snippet, language specific limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple rule: start with 10 to 20 pages you would be proud to show a customer, not 10,000. Before you publish a page, check four things. Does it answer a question someone really asks? Does it have code or numbers you verified yourself? Does it say something the other pages on the web do not? Would a developer who lands on it leave with what they came for? If any answer is no, fix the data first. Do not publish the page yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How do you know if AI tools are recommending your product?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can check this by hand in under an hour a month. Here is a simple routine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write down 20 questions real developers ask about your category. Pull them from support tickets, Reddit threads, and your Search Console queries.&lt;/li&gt;
&lt;li&gt;Once a month, ask each question in ChatGPT, Perplexity, and Google (including AI Mode). Use the same wording every time. Answers can change from one ask to the next, so ask the important ones a few times.&lt;/li&gt;
&lt;li&gt;For each answer, record four things: were you named, were you linked, which competitors were named, and which sites were cited.&lt;/li&gt;
&lt;li&gt;Look at the cited sites. Those are the pages shaping answers in your category, and they are your outreach list for step 5.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsg4f28zd2yuxk8m0lib3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsg4f28zd2yuxk8m0lib3.png" alt="llm" width="703" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then add the numbers your tools already give you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT referrals.&lt;/strong&gt; ChatGPT adds &lt;strong&gt;&lt;code&gt;utm_source=chatgpt.com&lt;/code&gt;&lt;/strong&gt; to links, so you can &lt;a href="https://help.openai.com/en/articles/12627856-publishers-and-developers-faq" rel="noopener noreferrer"&gt;**filter for it in&lt;/a&gt;**&amp;nbsp;any analytics tool, like Google Analytics or &lt;a href="https://raah.dev/" rel="noopener noreferrer"&gt;raah.dev&lt;/a&gt;, which tracks both web analytics and web observability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perplexity referrals.&lt;/strong&gt; Look for perplexity.ai in your referrer reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google.&lt;/strong&gt; Search Console has a &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Generative AI performance report&lt;/strong&gt;&lt;/a&gt;. Google also warns that no outside tool has access to its internal ranking or AI systems, so be careful with tools that claim otherwise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A free check.&lt;/strong&gt; Ahrefs offers a &lt;a href="https://ahrefs.com/ai-overviews-tracker" rel="noopener noreferrer"&gt;&lt;strong&gt;free AI Overviews tracker&lt;/strong&gt;&lt;/a&gt; that shows how often AI Overviews mention your brand and which sites they cite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expect slow movement. Crawler access can change within a day or so. Mentions across the web take much longer to build, so judge your progress by the quarter, not the week.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What mistakes keep developer products out of AI answers?&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blocking every AI bot.&lt;/strong&gt; Many teams block all of them to stop training and end up hiding from search too. Keep OAI-SearchBot and PerplexityBot allowed even if you block GPTBot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publishing hundreds of thin pages.&lt;/strong&gt; Google treats this as spam, and volume does not make a site better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buying or faking mentions and reviews.&lt;/strong&gt; Google says it is not as helpful as it seems, and fake reviews can hurt real trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hiding docs.&lt;/strong&gt; Login walls, JavaScript-only pages, and text inside images all make your best material hard to read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rewriting everything for robots.&lt;/strong&gt; Google says you do not need to chop content into tiny pieces or write in a special style for AI. Write for the developer reading it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judging by one test.&lt;/strong&gt; One answer on one day tells you very little. Track a fixed set of questions over months.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Frequently asked questions&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How do I get my product recommended by ChatGPT?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Allow OAI-SearchBot in your robots.txt, put a clear answer at the top of your homepage and docs, and get your product mentioned on sites AI tools trust, like Reddit and review platforms. &lt;a href="https://developers.openai.com/api/docs/bots" rel="noopener noreferrer"&gt;&lt;strong&gt;OpenAI says&lt;/strong&gt;&lt;/a&gt; sites that opt out of OAI-SearchBot will not be shown in ChatGPT search answers, so access is the first thing to check.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How long does it take to show up in AI answers?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Nobody can promise a date. &lt;a href="https://docs.perplexity.ai/docs/resources/perplexity-crawlers" rel="noopener noreferrer"&gt;&lt;strong&gt;Perplexity says&lt;/strong&gt;&lt;/a&gt; a robots.txt change can take up to 24 hours to show up, but building mentions across the web is slow. Plan in quarters, not weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Do backlinks still matter for AI visibility?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;They help, but less than mentions in the &lt;a href="https://ahrefs.com/blog/ai-overview-brand-correlation/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs study of 75,000 brands&lt;/strong&gt;&lt;/a&gt;: 0.218 for backlinks against 0.664 for brand mentions in AI Overviews. In &lt;a href="https://ahrefs.com/blog/chatgpts-most-cited-pages/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ahrefs' look at ChatGPT's most cited pages&lt;/strong&gt;&lt;/a&gt;, the ones that did rank mostly sat on strong sites, with 65.3% on domains rated 81 or higher. Both findings are correlations, so read them as hints.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Do I need an llms.txt file?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Not for Google, which &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;says its search ignores those files&lt;/strong&gt;&lt;/a&gt;. Adding one will not hurt, and other services may use it, but it should not come before the basics in this guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Do I need schema markup?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;&lt;strong&gt;Google says&lt;/strong&gt;&lt;/a&gt; no special markup is required for AI Overviews or AI Mode. If it is easy to add, keep it matched to the visible text on the page. Do not expect it to decide your results.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Should I block GPTBot?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;That is your call. &lt;a href="https://developers.openai.com/api/docs/bots" rel="noopener noreferrer"&gt;&lt;strong&gt;OpenAI treats&lt;/strong&gt;&lt;/a&gt; GPTBot, used for training, and OAI-SearchBot, used for search, as separate settings. You can block the first and still allow the second.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Is AEO different from SEO?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Mostly, it overlaps. &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;&lt;strong&gt;Google calls&lt;/strong&gt;&lt;/a&gt; optimizing for generative AI search a form of SEO. The extra work is off your site: getting discussed in trusted places, and making each page answer its question clearly in the first few lines.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Claude Opus 5.5: What Changed, What Breaks, and Whether to Switch</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:03:33 +0000</pubDate>
      <link>https://dev.to/studio1hq/claude-opus-55-what-changed-what-breaks-and-whether-to-switch-3oa5</link>
      <guid>https://dev.to/studio1hq/claude-opus-55-what-changed-what-breaks-and-whether-to-switch-3oa5</guid>
      <description>&lt;p&gt;In September 2026, Anthropic's CEO Dario Amodei &lt;a href="https://darioamodei.com/post/we-must-pace-the-frontier" rel="noopener noreferrer"&gt;published an essay&lt;/a&gt; with this line in it: "We must slow the pace at which we improve the capabilities of AI models."&lt;/p&gt;

&lt;p&gt;A week after, on September 22, 2026, Anthropic &lt;a href="https://www.anthropic.com/claude-opus-5-5" rel="noopener noreferrer"&gt;released Claude Opus 5.5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It landed just three weeks after Fable 5.1, so people had jokes. But if you write code for a living, the actual questions are something else. What does it cost you, what will it break, and is it better at your actual work?&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;If you want the quick take, Opus 5.5 is what Opus 5 should have shipped as, and Anthropic finally listened.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't ramble anymore.&lt;/strong&gt; Opus 5 used to pad every answer with fluff. This one gets to the point, and it's about 30% quicker to do so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's cheaper, and the savings actually hold up.&lt;/strong&gt; $4 in and $20 out per million tokens, down from $5 and $25. Just don't crank the thinking effort all the way up, or the savings disappear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It comes close to Fable 5.1 on coding work,&lt;/strong&gt; which is Anthropic's priciest model, at less than half the cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A few things break if you're already using Opus 5.&lt;/strong&gt; Nothing that ruins your day, but enough that you can't just flip the switch and forget about it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're on Opus 5 right now, this is worth moving to. Try it on your own work first though, the real difference only shows up once you compare it against what you're actually building, not the benchmark charts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Opus 5.5?
&lt;/h3&gt;

&lt;p&gt;Claude has 3 families of Models. Haiku, Sonnet, and Opus. Then there's Fable, a separate tier above all three that costs the most.&lt;/p&gt;

&lt;p&gt;Opus 5.5 is built for agents that run for hours. It has a 1M token context window and a 128K token output limit. Its knowledge runs up to June 2026.&lt;/p&gt;

&lt;p&gt;It's the first model in a new 5.5 family. Sonnet 5.5 and Haiku 5.5 are due in the next few weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it differs from the other models
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;Opus 5&lt;/th&gt;
&lt;th&gt;Fable 5.1&lt;/th&gt;
&lt;th&gt;Sonnet 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-opus-5-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-opus-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-fable-5-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-sonnet-5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Input / output price&lt;/strong&gt; (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$4 / $20&lt;/td&gt;
&lt;td&gt;$5 / $25&lt;/td&gt;
&lt;td&gt;$10 / $50&lt;/td&gt;
&lt;td&gt;$2 / $10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Cache reads&lt;/strong&gt; (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;not listed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;About 30% slower than 5.5&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context / max output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1M / 128K&lt;/td&gt;
&lt;td&gt;1M / not listed&lt;/td&gt;
&lt;td&gt;1M / 128K&lt;/td&gt;
&lt;td&gt;1M / 128K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Safety filters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cyber and biology&lt;/td&gt;
&lt;td&gt;Cyber&lt;/td&gt;
&lt;td&gt;Cyber and biology&lt;/td&gt;
&lt;td&gt;not listed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal-Bench 4.0&lt;/strong&gt; (coding)&lt;/td&gt;
&lt;td&gt;66.4%&lt;/td&gt;
&lt;td&gt;52.3%&lt;/td&gt;
&lt;td&gt;55.8%&lt;/td&gt;
&lt;td&gt;not tested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Long coding jobs, big refactors, reports&lt;/td&gt;
&lt;td&gt;Same jobs, wordier&lt;/td&gt;
&lt;td&gt;The hardest reasoning&lt;/td&gt;
&lt;td&gt;Quick, simple, high-volume work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compared with Opus 5&lt;/strong&gt;, the biggest change is how it talks. Opus 5 wrote long, twisty answers and did extra work nobody asked for. Opus 5.5 puts the main point first, writes about 30% faster, and costs less, with cache reads down from $0.50 to $0.20. That last part adds up when an agent re-reads the same files all day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Against Fable 5.1&lt;/strong&gt;, it holds its own for a lot less money. It costs 60% less and beats Fable on most coding tests, including Terminal-Bench 4.0 (66.4% to 55.8%). Fable is still the one to move up to when the hardest problems get past Opus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compared to Sonnet 5&lt;/strong&gt;, it's the heavier tool. Sonnet is half the price and faster, so it's the better pick for small edits, summaries, and routing. Reach for Opus 5.5 when the job is long or tricky.&lt;/p&gt;

&lt;h3&gt;
  
  
  What breaks when you switch
&lt;/h3&gt;

&lt;p&gt;Some requests that worked on Opus 5 now return errors. The &lt;a href="https://platform.claude.com/docs/en/models/opus-5-5/whats-new-opus-5-5" rel="noopener noreferrer"&gt;full list is in the docs&lt;/a&gt;. These are the four you'll actually hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Thinking can't be turned off.&lt;/strong&gt; Sending &lt;code&gt;thinking: {"type": "disabled"}&lt;/code&gt; or a manual token budget returns a 400. Leave it out, or use adaptive, and control depth with &lt;code&gt;effort&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forced tool use is gone.&lt;/strong&gt; &lt;code&gt;tool_choice&lt;/code&gt; set to &lt;code&gt;any&lt;/code&gt; or a named tool returns a 400. Use &lt;code&gt;auto&lt;/code&gt; with strict tool use, and say in the prompt when the tool should run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress messages can go quiet.&lt;/strong&gt; The short notes the model writes between tool calls now arrive as thinking blocks, which are empty by default. A UI that streams them will look frozen, with no error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The old computer use tool is rejected.&lt;/strong&gt; On the Claude API and Google Cloud, &lt;code&gt;computer_20251124&lt;/code&gt; returns a 400. Use &lt;code&gt;computer_toolset_20260801&lt;/code&gt;. Bedrock still accepts the old one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's one more, and it only affects newer accounts. Thinking blocks are tied to the conversation, so editing anything earlier in the conversation before replaying them returns an error. Keep conversations append-only.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it will really cost you
&lt;/h3&gt;

&lt;p&gt;Anthropic says the average job costs about &lt;a href="https://www.anthropic.com/claude-opus-5-5" rel="noopener noreferrer"&gt;40% less than on Opus 5&lt;/a&gt;. That holds at the default effort, which is now medium.&lt;/p&gt;

&lt;p&gt;At max effort it doesn't. An independent test found the model writes about 119,000 tokens per task there, against about 73,000 for Opus 5. The price cuts cancel that out, so the cost per task came out flat at roughly $6.&lt;/p&gt;

&lt;p&gt;It also thinks harder at each effort level than Opus 5 did. So don't copy your old setting over. Re-run a sweep on your own tasks and compare cost per finished task, not price per token.&lt;/p&gt;

&lt;p&gt;A few other prices, if you're budgeting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batch:&lt;/strong&gt; half price, $2 in and $10 out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast mode:&lt;/strong&gt; up to 2.5x speed at $8 in and $40 out. Claude API only, and still a research preview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI:&lt;/strong&gt; GPT-6 Astra is reported at $10 and $50, the same as Fable. The cheaper GPT-6 Sol costs far less per task but scored well below Opus 5.5 in the same test.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Test Scores
&lt;/h3&gt;

&lt;p&gt;Benchmarks are standard exams for models. These are the ones that matter most for dev work (best score in bold):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;What it checks&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;Fable 5.1&lt;/th&gt;
&lt;th&gt;Opus 5&lt;/th&gt;
&lt;th&gt;GPT-6 Astra&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terminal-Bench 4.0&lt;/td&gt;
&lt;td&gt;Multi-step tasks in a command line&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;66.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;55.8%&lt;/td&gt;
&lt;td&gt;52.3%&lt;/td&gt;
&lt;td&gt;57.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FrontierCode&lt;/td&gt;
&lt;td&gt;Would the code change get merged&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50.3%&lt;/td&gt;
&lt;td&gt;48.0%&lt;/td&gt;
&lt;td&gt;53.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CursorBench 4.0&lt;/td&gt;
&lt;td&gt;Real tasks from Cursor users&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;57.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;51.8%&lt;/td&gt;
&lt;td&gt;46.6%&lt;/td&gt;
&lt;td&gt;not reported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld 2.0&lt;/td&gt;
&lt;td&gt;Using a computer by clicking and typing&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;81.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;80.7%&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;td&gt;not reported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AutomationBench&lt;/td&gt;
&lt;td&gt;Business tasks across many apps&lt;/td&gt;
&lt;td&gt;40.0%&lt;/td&gt;
&lt;td&gt;31.4%&lt;/td&gt;
&lt;td&gt;26.9%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41.4%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal-Bench-Science&lt;/td&gt;
&lt;td&gt;Science research tasks&lt;/td&gt;
&lt;td&gt;58.7%&lt;/td&gt;
&lt;td&gt;52.6%&lt;/td&gt;
&lt;td&gt;29.0%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;64.6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are best-case scores, mostly from the maker's own runs. Even Anthropic says the real gap to Fable is smaller than the table looks.&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%2Fov69o75a30ok2b1ghdqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fov69o75a30ok2b1ghdqg.png" alt="Article-preview" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What people have built with it
&lt;/h3&gt;

&lt;p&gt;These are some great use cases built by people who got their hands on Opus 5.5 in the first two days. Most came from a single prompt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sketch to simulator.&lt;/strong&gt; &lt;a href="https://twitter.com/poolio/status/2102445641205248145" rel="noopener noreferrer"&gt;Ben Poole&lt;/a&gt;, formerly of Google Brain and DeepMind, drew a trebuchet on paper and sent Opus 5.5 a photo. It gave him a working 3D simulator where you change the weight and angle, fire at a stack of blocks, and replay the shot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minecraft in a browser tab.&lt;/strong&gt; &lt;a href="https://twitter.com/noahwachnik/status/2102470200415166699" rel="noopener noreferrer"&gt;Noah Wachnik&lt;/a&gt; asked for a playable Minecraft with fancy shaders. At max effort it built a game called Lumen Vale in 1 hour 37 minutes. You can walk around, break and place blocks, and the water ripples while the lighting changes with the time of day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pixel art wizard from a strict spec.&lt;/strong&gt; &lt;a href="https://twitter.com/majidmanzarpour/status/2102476258948927543" rel="noopener noreferrer"&gt;Majid&lt;/a&gt; wrote a tight prompt: one HTML file, 128x96 resolution, 24 colors, particle effects, and a state machine for idle, charge and cast. It followed the whole spec and runs at 60 fps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An 80-second film with no libraries.&lt;/strong&gt; &lt;a href="https://twitter.com/LCSlates/status/2102503027340988559" rel="noopener noreferrer"&gt;Chris Riley&lt;/a&gt; wanted a film in one HTML file using only WebGL2 and plain JavaScript. No libraries, no images, no audio files. The result is a glass mosaic where the fish and birds are made of moving tiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Blender castle, with the bill.&lt;/strong&gt; &lt;a href="https://twitter.com/Stefan_3D_AI/status/2102471841046786153" rel="noopener noreferrer"&gt;Stefan Vaskevich&lt;/a&gt; had Opus 5.5 and GPT-6 Astra each build a 10-second Blender animation of a castle from one prompt. Opus took 35 minutes and cost about $13.30. Astra was faster at 28 minutes but cost $14.50. Stefan felt Opus handled more complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are demos people chose to post, so treat them as best cases. &lt;/p&gt;

&lt;h3&gt;
  
  
  Where it still loses
&lt;/h3&gt;

&lt;p&gt;Not everything's better. A few real gaps before you switch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coding.&lt;/strong&gt; Sonar found it writes about 27% less code, with 42% fewer issues overall. But bugs per line went up 12%, and problems in concurrent code went up 44%. If your codebase leans on threads, locks, or async, don't skip the review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motion graphics and creative work.&lt;/strong&gt; Opus 5.5 is also being used for motion graphics and creative coding, with people generating animations from simple prompts. We have created &lt;a href="https://x.com/Astrodevil_/status/2103600734017392708?s=20" rel="noopener noreferrer"&gt;one ourselves&lt;/a&gt; using a single prompt. The model goes beyond code generation into actually building visual experiences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business tasks and science.&lt;/strong&gt; OpenAI's Astra still wins both. It beats Opus 5.5 on business tasks, and by a wider margin, on science.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard reasoning.&lt;/strong&gt; Fable still wins here. If your evals fail at high effort, that's your next move, not a higher setting on Opus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; It's slow to start. One test clocked about 22 seconds before the first word at medium effort. Fine for a background job, bad for a chat window someone's watching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and biology.&lt;/strong&gt; Some work gets rerouted without asking. Most security tasks, like finding exploits, go to the older Opus 4.8. Flagged biology requests go to Opus 5. The filters read your files and search results too, so something already sitting in your repo can trigger a reroute. Worth checking if you build anything security or bio adjacent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple, everyday tasks.&lt;/strong&gt; Sonnet 5 is still the better call here. Cheaper, faster, no reason to reach for Opus.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Opus 5.5 is basically Opus 5 with some parts fixed. Anthropic heard the complaints about Opus 5 and cheaper, faster, less bloated is the result.&lt;/p&gt;

&lt;p&gt;It's not for everyone. Security, biology, or low-latency work still needs a second look, and a few breaking changes mean this isn't a plug-and-play swap.&lt;/p&gt;

&lt;p&gt;Test it on your own tasks before you switch. That's the only benchmark that matters.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>What is Jev? In 5 minutes</title>
      <dc:creator>Nancy Garg</dc:creator>
      <pubDate>Tue, 06 Oct 2026 18:59:13 +0000</pubDate>
      <link>https://dev.to/studio1hq/what-is-jev-in-5-minutes-3jij</link>
      <guid>https://dev.to/studio1hq/what-is-jev-in-5-minutes-3jij</guid>
      <description>&lt;p&gt;&lt;a href="https://typesafe.ai/" rel="noopener noreferrer"&gt;TypeSafe&lt;/a&gt; AI released a new model called &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;Jev on September 15, 2026&lt;/a&gt;, and it works differently from most AI models you've probably used.&lt;/p&gt;

&lt;p&gt;Instead of writing you an answer, it picks one from a list you give it, and tells you how sure it is about that pick. TypeSafe has named it &lt;a href="https://docs.typesafe.ai/concepts/system-one" rel="noopener noreferrer"&gt;System One&lt;/a&gt; model.&lt;/p&gt;

&lt;p&gt;Most of the time when software uses AI to make a decision, like sorting a support ticket or flagging a transaction, it doesn't actually need a written answer, it need few outcomes.&lt;/p&gt;

&lt;p&gt;But an LLM throws a full paragraph and then you have to write code to pull out the actual decision. But Jev skips all of that and gives you the decision directly. &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%2Fttq88zgsfw066yeo211d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fttq88zgsfw066yeo211d.png" alt=" " width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Jev takes a piece of text (&lt;code&gt;state&lt;/code&gt;) plus one or more typed questions, and returns a typed answer with calibrated probabilities. Never free-form text.&lt;/li&gt;
&lt;li&gt;Three answer types: &lt;strong&gt;Choice&lt;/strong&gt; (pick one of N options), &lt;strong&gt;Score&lt;/strong&gt; (rate on an ordered scale), &lt;strong&gt;Noul&lt;/strong&gt; (probability that a yes/no statement is true).&lt;/li&gt;
&lt;li&gt;It's fast (well under a second in most cases) and cheaper $0.042 per million input tokens, output is free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What exactly is Jev?
&lt;/h2&gt;

&lt;p&gt;Jev is built by TypeSafe AI, started by &lt;a href="https://x.com/CompleteSkeptic/status/2099925682726002904?s=20" rel="noopener noreferrer"&gt;Diogo Almeida&lt;/a&gt;, the person who worked on ChatGPT and RLHF at OpenAI, along with Erik Gafni and Sasha Sheng.&lt;/p&gt;

&lt;p&gt;The way it works is pretty simple once you see it. You give it a piece of text, called the &lt;a href="https://docs.typesafe.ai/concepts/state" rel="noopener noreferrer"&gt;state&lt;/a&gt;, and then you ask it a question that has a fixed set of possible answers. It reads the text and answers the question, along with a number that says how confident it is. It gives a answer in 70 to 500 milliseconds. &lt;/p&gt;

&lt;p&gt;There are three &lt;a href="https://docs.typesafe.ai/primitives" rel="noopener noreferrer"&gt;kinds of questions it can answer&lt;/a&gt;. You can ask it to pick one option out of a list, give a score on a scale, or answer yes or no as a probability. And you can send it several questions about the same piece of text, and it answers all of them together in one go.&lt;/p&gt;

&lt;p&gt;A real example: asking it whether a support ticket needs a refund on a short message cost about $0.00002. Run that on a million similar tickets, and the total comes to around $19. &lt;/p&gt;

&lt;h2&gt;
  
  
  How Jev Is different
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.typesafe.ai/models" rel="noopener noreferrer"&gt;Jev&lt;/a&gt; doesn’t replace them. They do different jobs. Here is a quick comparison between Jev and standard LLM&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;GPT / Claude&lt;/th&gt;
&lt;th&gt;Jev&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it gives you&lt;/td&gt;
&lt;td&gt;Written text&lt;/td&gt;
&lt;td&gt;A picked answer with a confidence score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How fast&lt;/td&gt;
&lt;td&gt;A few seconds&lt;/td&gt;
&lt;td&gt;Under a second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input cost&lt;/td&gt;
&lt;td&gt;Around $10 per million tokens&lt;/td&gt;
&lt;td&gt;$0.042 per million tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output cost&lt;/td&gt;
&lt;td&gt;You pay for every word&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it write essays or code&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it have a conversation&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it be wrong&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes, but it can never give an answer outside the options you gave it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is worth pausing on. Jev being unable to go outside the list of answers you gave it doesn't mean it's always right, it just means it can't get creative in a way that breaks your code. If you told it the answer has to be low, medium, or high, it will never hand you back something else, but it can still confidently pick medium when the real answer was low.&lt;/p&gt;

&lt;p&gt;TypeSafe's own number is 193x faster and 444x cheaper on their internal benchmark, self-run and openly flagged as the high end. &lt;/p&gt;

&lt;p&gt;One more practical detail: it can only read text, so no images or audio, and it can handle roughly 32,000 tokens per question, or about 64,000 tokens total once you count the input plus the question itself.&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%2Fhoeukg6mkiktapog3ent.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhoeukg6mkiktapog3ent.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern: Claude thinks, Jev decides
&lt;/h2&gt;

&lt;p&gt;Once you actually sit with how Jev is &lt;a href="https://forkast.news/typesafe-ais-jev-is-not-an-llm-and-that-may-be-the-point/" rel="noopener noreferrer"&gt;meant to be used&lt;/a&gt;, a pattern shows up everywhere. You keep a model like Claude or GPT around for the parts that need thinking or writing, and you hand Jev the small decision sitting in between.&lt;/p&gt;

&lt;p&gt;A task comes in. Claude or GPT reads it and works out what needs to happen, maybe drafts a reply, maybe explains something. Jev sits next to that, and every time there's a small decision in the road, which row is urgent, whether to brake, whether a tool call is even needed, Jev picks the answer in a fraction of a second, and the app acts on it.&lt;/p&gt;

&lt;p&gt;The rule that falls out of this is simple: if you're making a model write out a full explanation just to arrive at one of three or four possible outcomes, you're paying for an essay when all you needed was a checkbox.&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%2Fiihvmbdxjix0bptgkg4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiihvmbdxjix0bptgkg4c.png" alt=" " width="428" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usecase
&lt;/h2&gt;

&lt;p&gt;Here are a &lt;a href="https://docs.typesafe.ai/concepts/use-case-map" rel="noopener noreferrer"&gt;few usecases&lt;/a&gt; that fit this shape well, the kind you'd normally build by prompting a chat model and parsing its reply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sorting leads.&lt;/strong&gt; Feed it a form submission or a call summary, and let it sort into hot, warm, or cold. Nobody has to read through every lead by hand, and the hot ones get called first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing support messages.&lt;/strong&gt; Give it an incoming email or chat message, and have it decide whether it goes to billing, technical, refunds, or gets flagged urgent. The right team sees it immediately instead of after someone manually sorts the queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flagging invoices for review.&lt;/strong&gt; Pass in the invoice details and let it mark each one clean, suspicious, or needs review. Your team only has to actually look at the handful that came back flagged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triaging incoming customer messages.&lt;/strong&gt; For a business getting messages through chat or WhatsApp, sort each one into booking, pricing question, complaint, or spam, so replies go out to the ones that matter first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choosing which model an agent should use.&lt;/strong&gt; Instead of burning tokens on a big model deciding whether to search, calculate, query a database, or hand off to a human, let Jev make that pick and save the expensive model for the actual task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tagging comments and content.&lt;/strong&gt; Sort incoming comments or reviews into positive, negative, question, or needs a reply, so nothing important gets buried in a pile you were never going to read in full anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick way to check if your use-case need Jev : Can the answer be one item picked from a short list? If yes, it fits. If the answer actually needs a sentence or two of explanation, that's still a job for LLM. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to start?
&lt;/h2&gt;

&lt;p&gt;If you want to actually try it, the path is short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Try Jev at &lt;a href="https://typesafe.ai/" rel="noopener noreferrer"&gt;typesafe.ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Try it in the playground first, give it a situation and three or four options, and see what it picks&lt;/li&gt;
&lt;li&gt;Move to the API once you've seen one decision work the way you expect&lt;/li&gt;
&lt;li&gt;Start with something small and repetitive, not your hardest decision. Pick the boring one you make fifty times a day&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At $0.042 per million input tokens with free output, testing this costs close to nothing.&lt;/p&gt;

&lt;p&gt;For reference you can check what people are already building using Jev &lt;a href="https://madewithjev.com/" rel="noopener noreferrer"&gt;here.&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  When not to use Jev?
&lt;/h2&gt;

&lt;p&gt;A few situations where you still have to use LLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need actual text back.&lt;/strong&gt; Emails, summaries, explanations, and other generated content are still better suited to LLMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't define the possible outputs.&lt;/strong&gt; Jev works with predefined decisions such as yes/no, choices, or scores, so the decision needs to be structured ahead of time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The decision is genuinely high stakes.&lt;/strong&gt; For money, health, or legal outcomes, use Jev to sort, score, or prioritize, but keep a person involved in the final decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You haven't tested it on your own data.&lt;/strong&gt; Before relying on Jev in production, test it against real examples from your use case and see where it gets decisions right or wrong.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Jev isn't trying to replace GPT or Claude. LLMs are still the better fit when you need writing, explanation, or open-ended reasoning.&lt;/p&gt;

&lt;p&gt;Jev targets a different problem: &lt;strong&gt;decisions that software needs to make repeatedly.&lt;/strong&gt; For true/false checks, classification, routing, scoring, and verification, returning a typed decision and confidence score can be more useful than generating text that your code then has to parse.&lt;/p&gt;

&lt;p&gt;It won't outperform LLMs at every decision task, but its combination of structured outputs, speed, cost, and calibrated confidence makes it an interesting new primitive for building AI-powered software.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Real-Time Excalidraw-like Collaborative Canvas using Velt MCP and Antigravity🎉</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Thu, 21 May 2026 16:43:40 +0000</pubDate>
      <link>https://dev.to/studio1hq/build-a-real-time-excalidraw-like-collaborative-canvas-using-velt-mcp-and-antigravity-j3f</link>
      <guid>https://dev.to/studio1hq/build-a-real-time-excalidraw-like-collaborative-canvas-using-velt-mcp-and-antigravity-j3f</guid>
      <description>&lt;p&gt;In this tutorial, we’ll build an Excalidraw-style collaborative whiteboard using Next.js, HTML5 Canvas, and Velt. You’ll add real-time features like live cursors, comments, presence, and huddles directly into your app. Instead of wiring everything manually, we’ll use Velt MCP and AI agents to handle the integration. We’ll also look at how CRDT-based sync keeps everything in real time.&lt;/p&gt;

&lt;p&gt;By the end, you’ll have a fully working multi-user canvas app with production-ready collaboration built in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are building
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Excalidraw-style infinite whiteboard&lt;/li&gt;
&lt;li&gt;Real-time collaboration with cursors, comments, huddle, notifications, and presence&lt;/li&gt;
&lt;li&gt;Multi-user canvas with shared state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/UMjwaJklGUo" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why add collaboration to Canvas apps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single-user by default:&lt;/strong&gt; Most canvas apps work locally and don’t support multiple users out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time sync is complex:&lt;/strong&gt; Handling state sync, conflicts, and updates across users is not trivial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of shared context:&lt;/strong&gt; Without comments, cursors, and presence, collaboration feels disconnected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why use Velt
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://velt.dev/" rel="noopener noreferrer"&gt;Velt is a collaboration SDK&lt;/a&gt; that lets you add real-time, multi-user features directly into your app without building the backend infrastructure yourself. It handles presence, syncing, communication, and UI components out of the box, so you can focus on your product.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drop-in collaboration layer:&lt;/strong&gt; Add features like comments, cursors, and presence without building from scratch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time features built in:&lt;/strong&gt; Cursors, comments, presence, notifications, and huddles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRDT-based sync support:&lt;/strong&gt; Enables conflict-free real-time state updates for multi-user apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-powered setup with MCP:&lt;/strong&gt; Use Velt MCP and agent skills to automatically install and configure features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No infra needed:&lt;/strong&gt; No need to manage WebSockets, sync engines, or backend services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable UI components:&lt;/strong&gt; Easily integrate collaboration UI into your existing design system&lt;/li&gt;
&lt;/ul&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%2F6rjsf5dzzv7pmeg0kxds.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%2F6rjsf5dzzv7pmeg0kxds.png" alt="Velt landing page" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18+&lt;/li&gt;
&lt;li&gt;Velt API key (from Velt dashboard)&lt;/li&gt;
&lt;li&gt;AI coding editor (Anitgravity is used in this demo)&lt;/li&gt;
&lt;li&gt;Basic React and TypeScript knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up the project&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the repository - &lt;a href="https://github.com/Studio1HQ/Velt-Demos/tree/main/excalidraw-velt-demo" rel="noopener noreferrer"&gt;https://github.com/Studio1HQ/Velt-Demos/tree/main/excalidraw-velt-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;And then run &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now, open &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&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%2F9lu6s9xrt5379a35ehps.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%2F9lu6s9xrt5379a35ehps.png" alt="App canvas" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: Building Velt-powered Excalidraw-like App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Set up Velt MCP
&lt;/h3&gt;

&lt;p&gt;Velt MCP lets your editor (Antigravity) run the Velt installer and guide the integration.&lt;/p&gt;

&lt;p&gt;Now, add the Velt MCP installer to Antigravity using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @velt-js/mcp-installer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F4fk8nnegrtlz9iz150vc.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%2F4fk8nnegrtlz9iz150vc.png" alt="MCP installation" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add it to the Antigravity MCP server configuration with&amp;nbsp;&lt;strong&gt;&lt;code&gt;command: "npx"&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;and&amp;nbsp;&lt;strong&gt;&lt;code&gt;args: ["-y", "@velt-js/mcp-installer"]&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://docs.velt.dev/get-started/skills" rel="noopener noreferrer"&gt;Velt Agent Skills&lt;/a&gt; guides the AI on what to implement using best practices, while MCP gives it access to the tools needed to actually execute those changes. Together, they make the integration accurate, structured, and reliable. We have both installed and will be used accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get your Velt API key&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Velt Dashboard&lt;/li&gt;
&lt;li&gt;Create a project&lt;/li&gt;
&lt;li&gt;Copy your API key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add it to your &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;NEXT_PUBLIC_VELT_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_api_key_here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Start Velt installation using AI
&lt;/h3&gt;

&lt;p&gt;Now that MCP is set up, we can let the AI agent handle the Velt integration for us.&lt;/p&gt;

&lt;p&gt;Open your editor (Antigravity) and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install velt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This triggers the Velt MCP installer, which runs as a guided setup inside your editor.&lt;/p&gt;

&lt;p&gt;Instead of manually adding SDKs and wiring things, the agent walks you through the setup step by step.&lt;/p&gt;

&lt;p&gt;It will ask you for a few inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your &lt;strong&gt;project directory&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;Velt API key and auth token&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;features you want to enable&lt;/strong&gt; (comments, presence, cursors, CRDT, etc.)&lt;/li&gt;
&lt;li&gt;Where to place the &lt;strong&gt;VeltProvider&lt;/strong&gt; (recommended: &lt;code&gt;app/page.tsx&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;UI placement preferences (like corner position)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can answer each step directly in chat. The flow is simple and guided.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Provide the prompt for MCP
&lt;/h3&gt;

&lt;p&gt;At this point, we already have a working whiteboard built manually. Now, instead of integrating Velt step by step ourselves, we use Velt Agent Skills to analyze this existing app and plan how collaboration should be added.&lt;/p&gt;

&lt;p&gt;In your editor, after running &lt;code&gt;install velt&lt;/code&gt;, provide the following prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to start the Velt integration. Review my project structure and use your Velt Agent Skills to plan the CRDT store implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you provide this, the agent starts analyzing your codebase. It looks at how your canvas is structured, how state is managed, and where real-time sync can be introduced. Based on this, it generates an integration plan tailored to your whiteboard.&lt;/p&gt;

&lt;p&gt;Instead of manually deciding how to structure CRDT or where to wire Velt, the agent uses its skills to plan it correctly for your app.&lt;/p&gt;

&lt;p&gt;After reviewing the plan, you can approve it, and the agent will apply the changes step by step.&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%2F271ldj141xsyus6u0bqb.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%2F271ldj141xsyus6u0bqb.png" alt="Planing with agent" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Understand the existing project structure
&lt;/h3&gt;

&lt;p&gt;Before we look at what MCP added, let us understand how this project is structured. Since the agent analyzes your codebase before integrating Velt, this gives context for what it is working with.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/Studio1HQ/Velt-Demos/tree/main/excalidraw-velt-demo" rel="noopener noreferrer"&gt;project is organized into three main folders&lt;/a&gt;: &lt;code&gt;app&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt;, and &lt;code&gt;lib&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;app/&lt;/code&gt; folder contains the core application logic. This is where the whiteboard is rendered and all canvas interactions like drawing, selecting, and updating elements are handled.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;components/&lt;/code&gt; folder contains UI elements and collaboration integrations. This is where Velt features are connected to your app, including user identity, comments, and UI-level controls.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;lib/&lt;/code&gt; folder handles state management and shared logic. It manages canvas data, document context, and sync-ready state, making it easier to extend the app with real-time collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Add real-time collaboration features (Using MCP)
&lt;/h3&gt;

&lt;p&gt;Now that the whiteboard is working, we layer Velt on top to make it collaborative. This is where users start seeing each other, interacting in real time, and sharing context.&lt;/p&gt;

&lt;p&gt;After you provide the prompt and approve the plan, the MCP installer integrates Velt into your project. It sets up the foundation required for collaboration to work correctly with your existing whiteboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Presence and cursors
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/app/layout.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;app/layout.tsx&lt;/code&gt;&lt;/a&gt;, the &lt;code&gt;VeltProvider&lt;/code&gt; enables real-time awareness across your app. Then in &lt;code&gt;VeltSetup.tsx&lt;/code&gt;, each user is identified using. This is what allows Velt to track who is online.&lt;/p&gt;

&lt;p&gt;Learn more &lt;a href="https://docs.velt.dev/realtime-collaboration/presence/overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;VeltProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useSetDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VeltCursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useVeltClient&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@veltdev/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCurrentDocument&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="s2"&gt;@/lib/useCurrentDocument&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TEST_USERS&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="s2"&gt;@/lib/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSearchParams&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="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VeltIdentity&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;documentId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCurrentDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;useSetDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-whiteboard&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;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VeltProviderInner&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&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;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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;userIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIndex&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;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIndex&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fallback or default behavior&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;searchParams&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="nc"&gt;VeltProvider&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_VELT_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;authProvider&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VeltIdentity&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;/* &amp;lt;VeltCursor /&amp;gt; */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&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;VeltIdentity&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;VeltProvider&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VeltSetup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&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="kc"&gt;null&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;VeltProviderInner&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;children&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;VeltProviderInner&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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once identity is set, Velt automatically shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active users (avatars)&lt;/li&gt;
&lt;li&gt;Live cursor positions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t have to manually sync cursor movement. Velt handles that internally based on user sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Canvas comments
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.velt.dev/async-collaboration/comments/overview" rel="noopener noreferrer"&gt;Comments&lt;/a&gt; are one of the most important parts of a canvas app.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCommentAnnotations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VeltCommentPin&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="s2"&gt;@veltdev/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Point&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="s2"&gt;@/lib/types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWhiteboardStore&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="s2"&gt;@/lib/useWhiteboardStore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CanvasCommentLayerProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&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;In &lt;a href="https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/components/velt/CanvasCommentLayer.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;CanvasCommentLayer.tsx&lt;/code&gt;&lt;/a&gt;, comments are rendered as an overlay on top of the canvas. Instead of attaching comments to DOM elements, we attach them to canvas coordinates.&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;{&lt;/span&gt;
              &lt;span class="c1"&gt;// Use bounds or specific fields&lt;/span&gt;
              &lt;span class="c1"&gt;// Normalized rect provided by normalizeRect helper? not available here easily.&lt;/span&gt;
              &lt;span class="c1"&gt;// Just use raw coords if available, or approximate.&lt;/span&gt;
              &lt;span class="c1"&gt;// Rect/Ellipse/Diamond have x1,y1,x2,y2 usually?&lt;/span&gt;
              &lt;span class="c1"&gt;// Wait, types.ts says DrawingElement...&lt;/span&gt;
              &lt;span class="c1"&gt;// Let's assume standard shape properties&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;
              &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;worldX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;worldY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arrow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;worldX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="nx"&gt;worldY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;worldX&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;worldY&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&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="k"&gt;return&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worldX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;zoom&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;screenY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worldY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;zoom&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;div&lt;/span&gt;
            &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&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;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;screenX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;screenY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;translate(-50%, -100%)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;pointerEvents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&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="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;VeltCommentPin&lt;/span&gt; &lt;span class="na"&gt;annotationId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationId&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="nt"&gt;div&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;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&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;&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%2Fac8pvvv17lygyvnsmhau.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%2Fac8pvvv17lygyvnsmhau.png" alt="App canvas with comment" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From &lt;code&gt;app[/page.tsx](https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/app/page.tsx)&lt;/code&gt;, you trigger comments like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capture the &lt;code&gt;(x, y)&lt;/code&gt; position on click&lt;/li&gt;
&lt;li&gt;Pass context to Velt&lt;/li&gt;
&lt;li&gt;Optionally attach to a specific element using &lt;code&gt;elementId&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Freeform comments anywhere on the canvas&lt;/li&gt;
&lt;li&gt;Context-aware discussions linked to shapes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is very similar to how tools like Miro or Figma handle comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidebar and UI controls
&lt;/h3&gt;

&lt;p&gt;These files handle user-facing UI around collaboration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ProfileMenu.tsx&lt;/code&gt; shows user identity and active participants&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ThemeToggle.tsx&lt;/code&gt; syncs your app theme with Velt UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ProfileMenu.tsx&lt;/code&gt;&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChevronDown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LogOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RefreshCwIcon&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="s2"&gt;lucide-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// import { useVeltClient } from "@veltdev/react";&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;TEST_USERS&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="s2"&gt;@/lib/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfileMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCurrentUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Default to first user&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize from URL on mount&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&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;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;userIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIndex&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;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIndex&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setCurrentUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleUserSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;TEST_USERS&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="nf"&gt;setCurrentUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update URL and reload to ensure clean session isolation via VeltProvider authProvider&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;Velt components (like &lt;a href="https://docs.velt.dev/async-collaboration/comments-sidebar/overview" rel="noopener noreferrer"&gt;comments sidebar&lt;/a&gt;) automatically adapt to your app’s theme. This keeps the experience consistent across your UI and Velt overlays.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notifications and huddle
&lt;/h3&gt;

&lt;p&gt;Velt also provides built-in tools for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.velt.dev/async-collaboration/comments/notifications" rel="noopener noreferrer"&gt;Notifications (activity updates)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.velt.dev/realtime-collaboration/huddle/overview" rel="noopener noreferrer"&gt;Huddle (voice/video collaboration)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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%2F8f4bjxct7c5klvbsp217.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%2F8f4bjxct7c5klvbsp217.png" alt="Velt in action on app" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add these as UI buttons in your toolbar. Once added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notifications show real-time updates (comments, mentions, etc.)&lt;/li&gt;
&lt;li&gt;Huddle lets users start live audio/video sessions inside your app&lt;/li&gt;
&lt;/ul&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%2Ff0u4nx3cflc33hxy3zap.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%2Ff0u4nx3cflc33hxy3zap.png" alt="Velt huddles" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No extra backend setup is needed. These features are already part of the Velt SDK.&lt;/p&gt;

&lt;p&gt;You can see this here in &lt;a href="https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/app/page.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;page.tsx&lt;/code&gt;&lt;/a&gt;&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mx-1 h-6 w-px bg-slate-200 dark:bg-neutral-800"&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-1"&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;VeltCommentTool&lt;/span&gt; &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isDark&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;VeltHuddleTool&lt;/span&gt; &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isDark&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;VeltNotificationsTool&lt;/span&gt; &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isDark&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="nt"&gt;div&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: CRDT-based state sync
&lt;/h3&gt;

&lt;p&gt;Up to this point, the whiteboard works, and collaboration features like comments and presence are in place. But for a real collaborative canvas, the most important piece is shared state. Every shape, line, or text element needs to stay in sync across all users in real time.&lt;/p&gt;

&lt;p&gt;This is where CRDT comes in.&lt;/p&gt;

&lt;p&gt;In a typical canvas app, state lives locally. When a user draws something, it updates only their view. To make this collaborative, we need a shared state that all users can read and write to.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/lib/useWhiteboardStore.ts" rel="noopener noreferrer"&gt;&lt;code&gt;useWhiteboardStore.ts&lt;/code&gt;&lt;/a&gt;, instead of using local state, we use Velt’s CRDT hook:&lt;/p&gt;

&lt;p&gt;This creates a shared store that is automatically synced across all connected users.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; acts as a unique identifier for this piece of state. As long as users are in the same document, they are all connected to this store. The &lt;code&gt;map&lt;/code&gt; structure is used to store canvas elements, where each element is indexed by its ID.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useVeltCrdtStore&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="s2"&gt;@veltdev/crdt-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DrawingElement&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="s2"&gt;./types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElementMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DrawingElement&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWhiteboardStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useVeltCrdtStore&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementMap&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whiteboard-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DrawingElement&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&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="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&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;valueRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementMap&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;value&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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;valueRef&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;value&lt;/span&gt; &lt;span class="o"&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user draws a new shape or updates an existing one, the change is not stored locally. Instead, it is written to the CRDT store.&lt;/p&gt;

&lt;p&gt;Functions like adding or updating elements internally call the &lt;code&gt;update&lt;/code&gt; function provided by &lt;code&gt;useVeltCrdtStore&lt;/code&gt;. Once updated, the change is automatically propagated to every other user connected to the same session.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&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;DrawingElement&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;valueRef&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="nx"&gt;el&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="nx"&gt;el&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;update&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;updateElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&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;DrawingElement&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;valueRef&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="nx"&gt;el&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="nx"&gt;el&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;update&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;deleteElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&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="kr"&gt;string&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;next&lt;/span&gt; &lt;span class="o"&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;valueRef&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="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;next&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&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;update&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rawMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need to manage WebSockets, events, or manual syncing. Velt handles all of that behind the scenes.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/Studio1HQ/Velt-Demos/blob/main/excalidraw-velt-demo/lib/storageProxy.ts" rel="noopener noreferrer"&gt;&lt;code&gt;storageProxy.ts&lt;/code&gt;&lt;/a&gt; file acts as a thin abstraction layer between your canvas logic and the shared store. Instead of directly interacting with the CRDT store everywhere, this layer helps keep the code clean and organized.&lt;/p&gt;

&lt;p&gt;It separates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Canvas logic&lt;/li&gt;
&lt;li&gt;State update logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the system easier to maintain and extend.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Validates if the code is running in a browser environment
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Proxies localStorage to redirect specific keys to sessionStorage
 * This allows Velt to have isolated sessions per tab (using sessionStorage)
 * while the rest of the app continues to use localStorage.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initStorageProxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isBrowser&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="c1"&gt;// Store the original localStorage implementation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalLocalStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&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;originalSessionStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Keys that should be redirected to sessionStorage&lt;/span&gt;
  &lt;span class="c1"&gt;// Velt uses keys starting with 'velt', 'snippyly', or '_v' (e.g., _viu, _vv)&lt;/span&gt;
  &lt;span class="c1"&gt;// Also proxying firebase keys as Velt likely uses them for auth/presence&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isVeltKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="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;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;velt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;snippyly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_v&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase&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;h3&gt;
  
  
  Step 7: Multi-user simulation
&lt;/h3&gt;

&lt;p&gt;The user setup lives in &lt;code&gt;lib/users.ts&lt;/code&gt;. This file defines a small set of users with basic details like name, color, and avatar. These are used by Velt to represent each participant across features like cursors, comments, and presence.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;components/velt/VeltSetup.tsx&lt;/code&gt;, one of these users is selected when the app loads. A random user is picked and passed to Velt. This is what establishes the session.&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TEST_USERS&lt;/span&gt; &lt;span class="o"&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;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Robin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;robin@velt.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;photoUrl&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://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=800&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;q=60&amp;amp;ixlib=rb-4.1.0&amp;amp;ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8cG9ydHJhaXR8ZW58MHx8MHx8fDA%3D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#F97316&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Orange&lt;/span&gt;
    &lt;span class="na"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;excalidraw-demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alicia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alicia@velt.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;photoUrl&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://images.unsplash.com/photo-1531746020798-e6953c6e8e04?q=80&amp;amp;w=1364&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;ixlib=rb-4.1.0&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3B82F6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
    &lt;span class="na"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;excalidraw-demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time the app loads, a different user can be assigned. From Velt’s perspective, each session is now a unique participant in the same document.&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%2F3asgblmz459i51j7xh65.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%2F3asgblmz459i51j7xh65.png" alt="User switch from canvas UI" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Run and test the app
&lt;/h3&gt;

&lt;p&gt;Now let’s run the app and see everything working together.&lt;/p&gt;

&lt;p&gt;Start the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the app is running, open it in your browser at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To test collaboration, open the same app in another window. You can use an incognito tab or a different browser. Each session will act as a different user.&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%2Fpetdhw0m4w30twjlj9ld.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%2Fpetdhw0m4w30twjlj9ld.png" alt="Side by side comparison of app functionality" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you interact with the canvas, you’ll start noticing the real-time behavior. Drawing a shape in one window reflects instantly in the other. You’ll see users appearing in presence, and comments syncing across both sessions.&lt;/p&gt;

&lt;p&gt;At this point, your whiteboard is fully collaborative, with multiple users interacting on the same canvas in real time.&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%2Fuzds4h1vrij7qghrgfei.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%2Fuzds4h1vrij7qghrgfei.png" alt="Multiple users interacting with our app in real-time" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://velt-excalidraw.vercel.app/?docId=wb-1774601086195-xxyr2ux&amp;amp;user=0" rel="noopener noreferrer"&gt;Here is the deployed link&lt;/a&gt; for you to explore the demo. &lt;/p&gt;

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

&lt;p&gt;We started with a simple canvas and turned it into a fully collaborative whiteboard. Along the way, we added real-time cursors, presence, comments, notifications, and even voice huddles using &lt;a href="https://velt.dev/" rel="noopener noreferrer"&gt;Velt&lt;/a&gt;. Instead of building sync logic and infra from scratch, we used &lt;a href="https://docs.velt.dev/mcp/mcp" rel="noopener noreferrer"&gt;Velt MCP and agent skills&lt;/a&gt; to handle the heavy lifting and get everything working quickly.&lt;/p&gt;

&lt;p&gt;The key takeaway here is simple. You don’t need to spend weeks building real-time systems to make your app collaborative. With the right tools, you can focus on your product and let the platform handle sync, presence, and communication.&lt;/p&gt;

&lt;p&gt;From here, you can extend this further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect real authentication instead of simulated users&lt;/li&gt;
&lt;li&gt;Improve CRDT logic for more complex canvas operations&lt;/li&gt;
&lt;li&gt;Customize Velt UI components to match your product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re building any product where users need to collaborate, this is a pattern worth exploring.&lt;/p&gt;

&lt;p&gt;What would your app look like if it supported real-time collaboration from day one?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/velt-js" rel="noopener noreferrer"&gt;Try Velt and start adding features&lt;/a&gt; like comments, presence, cursors, CRDT sync, notifications, and huddles to your app.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Zulip Style Collaborative Chat App with Next.js and Velt</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Tue, 05 May 2026 17:43:33 +0000</pubDate>
      <link>https://dev.to/studio1hq/building-a-zulip-style-collaborative-chat-app-with-nextjs-and-velt-2mcp</link>
      <guid>https://dev.to/studio1hq/building-a-zulip-style-collaborative-chat-app-with-nextjs-and-velt-2mcp</guid>
      <description>&lt;p&gt;Zulip is known for keeping conversations organized. Topic-based threads, clear context, and async-friendly discussions make it a favorite for technical and distributed teams. Unlike traditional chat apps, conversations in Zulip stay readable even as teams scale.&lt;/p&gt;

&lt;p&gt;Recreating this experience can be a bit difficult. Real-time messaging, user presence, inline comments, and notifications usually require a complex backend and real-time infrastructure.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will build a Zulip-style collaborative chat application using Next.js, Tailwind CSS, and Velt. Next.js powers the UI and application structure, while Velt adds collaboration features like presence, comments, and notifications without writing backend code.&lt;/p&gt;

&lt;p&gt;By the end, you will have a working chat interface inspired by Zulip, with real-time collaboration built in and ready to extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Zulip’s Approach Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://zulip.com/" rel="noopener noreferrer"&gt;Zulip’s design&lt;/a&gt; solves a problem most chat tools struggle with: conversations getting messy over time.&lt;/p&gt;

&lt;p&gt;Instead of long, linear message streams, Zulip organizes discussions into topics. Each message belongs to a clear context, so conversations stay focused and easy to follow even days or weeks later.&lt;/p&gt;

&lt;p&gt;Three ideas make this work especially well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time collaboration:&lt;/strong&gt; Messages, comments, and updates appear instantly for everyone in the channel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-rich discussions:&lt;/strong&gt; Replies stay tied to a specific topic or message, so feedback does not get lost in the noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visible presence:&lt;/strong&gt; You always know who is online and actively participating, which makes collaboration feel immediate and shared.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What makes this challenging to build in a SaaS product is not the UI, but the infrastructure behind it. Real-time messaging, presence tracking, comments, and notifications typically require WebSockets, backend event systems, data synchronization, and careful handling of concurrent users. Building and maintaining this reliably can take significant engineering effort.&lt;/p&gt;

&lt;p&gt;In this tutorial, we recreate Zulip’s collaborative behavior without building that infrastructure ourselves, by using Velt as the collaboration layer.&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%2Fiwuxo9h5r3v74v6r8ilz.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%2Fiwuxo9h5r3v74v6r8ilz.png" alt="Zulip Chat" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We’re Building With
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, let’s quickly look at the tools we’ll use and why they fit this project well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js:&lt;/strong&gt; Next.js gives us a solid foundation for building interactive applications. With the App Router, we get a clean layout structure and client-side interactivity that works well for chat-style interfaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React:&lt;/strong&gt; React handles the UI composition and state updates. The chat interface, message list, and user interactions all rely on simple, predictable React patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS:&lt;/strong&gt; Tailwind helps us build a clean and modern UI quickly. It keeps styling close to the components and makes it easy to adjust layouts, spacing, and themes without writing custom CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;shadcn ui (powered by Radix UI):&lt;/strong&gt; These provide accessible, reusable UI primitives like buttons, dropdowns, and avatars. They give us a polished look without locking us into heavy component libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zustand:&lt;/strong&gt; Zustand is used for lightweight state management. In this project, it manages demo users and allows us to switch between them to test real-time collaboration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Velt:&lt;/strong&gt; &lt;a href="https://velt.dev/" rel="noopener noreferrer"&gt;Velt&lt;/a&gt; is the key piece. Instead of building real-time infrastructure ourselves, Velt provides presence, comments, notifications, and collaboration out of the box. Once integrated, features like reactions, read status, and threaded comments work automatically without writing extra backend or real-time code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, this stack lets us focus on the chat experience and UI, while Velt handles the collaboration layer behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js (v16 or later)&lt;/strong&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;Basic familiarity with Next.js, React, and TypeScript&lt;/li&gt;
&lt;li&gt;A Velt account (you can sign up for free at &lt;a href="http://velt.devhttps://velt.dev/" rel="noopener noreferrer"&gt;velt.dev&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Working knowledge of &lt;strong&gt;Tailwind CSS&lt;/strong&gt; fundamentals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prior experience with Velt&lt;/li&gt;
&lt;li&gt;Any backend or database setup&lt;/li&gt;
&lt;li&gt;Experience building real-time systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll walk through the collaboration setup step by step, and the app runs entirely on the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Project Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of scaffolding a new project from scratch, we’ll start from an existing Zulip style chat app that already has the UI and collaboration logic wired up. This lets us focus on understanding how the pieces fit together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone the Repository
&lt;/h3&gt;

&lt;p&gt;Begin by cloning the project and moving into the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gitclone https://github.com/Studio1HQ/zulip-velt
&lt;span class="nb"&gt;cd &lt;/span&gt;zulip-velt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Install all required dependencies using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install Next.js, Tailwind CSS, shadcn ui components, Zustand for state management, and the Velt SDK used for collaboration features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Velt API Key
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://console.velt.dev/" rel="noopener noreferrer"&gt;Velt requires a public API key&lt;/a&gt; to enable collaboration features like presence, comments, and notifications.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file in the root of the project and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NEXT_PUBLIC_VELT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_velt_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can generate this key from the Velt dashboard after creating a free account.&lt;/p&gt;

&lt;p&gt;Once the key is added, restart the development server if it is already running.&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%2Fmv90vi7o2ow5ixcqlmzv.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%2Fmv90vi7o2ow5ixcqlmzv.png" alt="Velt dashboard" width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the App Locally
&lt;/h3&gt;

&lt;p&gt;Start the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see a Zulip-style chat interface with channels on the left, a message area in the center, and collaboration controls in the header.&lt;/p&gt;

&lt;p&gt;At this point, the UI is already functional. In the next sections, we’ll break down how the app is structured and how Velt is integrated to power real-time collaboration without any backend setup.&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%2Fxsas11ltx3o5lolidq3m.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%2Fxsas11ltx3o5lolidq3m.png" alt="Zulip chat2" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;At a high level, the project is divided into five main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;app&lt;/strong&gt;: Handles routing, layouts, and global configuration using the Next.js App Router&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;components&lt;/strong&gt;: Contains all reusable UI and chat-related components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;helper&lt;/strong&gt;: Manages demo user data and user switching logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hooks&lt;/strong&gt;: Stores custom React hooks such as theme management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lib&lt;/strong&gt;: Holds small utility functions used across the app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a closer look at the most important folders we’ll work with in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding the App Router and Layout Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This project uses the Next.js App Router, which organizes the application using layouts and pages instead of traditional route files. If you are new to the App Router, don’t worry. We’ll focus only on what matters for this app.&lt;/p&gt;

&lt;p&gt;The key idea is simple: layouts wrap pages, and this is where we place Velt and theme handling.&lt;/p&gt;

&lt;p&gt;In this project, there are two layout files, each with a different responsibility.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;layout.tsx&lt;/code&gt; is the root layout for the entire application. It defines the HTML structure, metadata, and global styles.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&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="s2"&gt;@/hooks/use-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VeltProvider&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="s2"&gt;@veltdev/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;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;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&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="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;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="nc"&gt;VeltProvider&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_VELT_ID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&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;ThemeProvider&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;children&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;ThemeProvider&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;VeltProvider&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;&lt;code&gt;page.tsx&lt;/code&gt; - This file renders the main chat interface. You’ll see it importing layout and chat components and placing them on the 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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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;Header&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;@/components/layout/header&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;Sidebar&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;@/components/layout/sidebar&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;ChatArea&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;@/components/chat/chat-area&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsMobile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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;checkMobile&lt;/span&gt; &lt;span class="o"&gt;=&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;mobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;
      &lt;span class="nf"&gt;setIsMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mobile&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;mobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setIsSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;checkMobile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkMobile&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkMobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleSidebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&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="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;isSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsSidebarCollapsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="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;closeSidebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-screen flex flex-col bg-background"&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;Header&lt;/span&gt; 
        &lt;span class="na"&gt;onToggleSidebar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleSidebar&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isMobile&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 flex overflow-hidden"&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;Sidebar&lt;/span&gt; 
          &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;isCollapsed&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isSidebarCollapsed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closeSidebar&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;ChatArea&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;div&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;div&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;ul&gt;
&lt;li&gt;
&lt;code&gt;app/layout.tsx&lt;/code&gt; is the global shell&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app/(app)/layout.tsx&lt;/code&gt; is where Velt is initialized&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.tsx&lt;/code&gt; is where the chat UI begins&lt;/li&gt;
&lt;li&gt;Providers are placed at layout level so all components can access them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Building the Chat Interface with Components&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Most of the Zulip-style experience in this app is built using reusable React components. Each part of the interface has a clear responsibility, which makes the code easier to read and extend.&lt;/p&gt;

&lt;p&gt;We’ll focus on three areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The chat area where messages appear&lt;/li&gt;
&lt;li&gt;The message component itself&lt;/li&gt;
&lt;li&gt;The layout components that shape the overall UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Chat Area:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;chat-area.tsx&lt;/code&gt; - This component is responsible for rendering the list of messages and attaching collaboration features to them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;message.tsx&lt;/code&gt; - Each message in the chat is rendered as its own component. This keeps the UI modular and makes it easy to attach collaboration features at the message level.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Layout Components:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The layout of the app is built using two main components: a header and a sidebar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;sidebar&lt;/strong&gt; represents Zulip style channels and navigation.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;header&lt;/strong&gt; is where collaboration controls live.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the header, you’ll see Velt components like:&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;VeltPresence&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;VeltNotificationsTool&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;VeltCommentsSidebar&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;VeltSidebarButton&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;&lt;strong&gt;What these do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VeltPresence&lt;/code&gt; shows who is currently online&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VeltNotificationsTool&lt;/code&gt; displays collaboration notifications&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VeltCommentsSidebar&lt;/code&gt; Opens a panel with all comments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VeltSidebarButton&lt;/code&gt; toggles the comments sidebar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these work automatically once Velt is initialized at the layout level.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;VeltPresence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VeltNotificationsTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VeltSidebarButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VeltCommentsSidebar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useVeltClient&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@veltdev/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useUserStore&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="s2"&gt;@/helper/userdb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HeaderProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;onToggleSidebar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;onToggleSidebar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HeaderProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useUserStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVeltClient&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;prevUserRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;isInitializingRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Prevent overlapping initialization calls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fh8ppbpvxlz56kxhsmhl6.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%2Fh8ppbpvxlz56kxhsmhl6.png" alt="Zulip dashboard" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Initialization, User Identification, and Document Setting&lt;/strong&gt;&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;// Handle Velt client initialization, user identification, and document setting&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;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;client&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isInitializingRef&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Velt init skipped:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;initializing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isInitializingRef&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initializeVelt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;isInitializingRef&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;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Detect user switch&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isUserSwitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prevUserRef&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="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;prevUserRef&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;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Starting Velt init for user:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isUserSwitch&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Re-identify the user (handles initial and switches)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;veltUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;organization_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;photoUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;photoUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;veltUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Velt user identified:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;veltUser&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDocuments&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zulip-velt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;documentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zulip-velt&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Velt documents set: zulip-velt&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error initializing Velt:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;isInitializingRef&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;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;initializeVelt&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;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Re-run on client or user change&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Reusable UI Components&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To keep the chat interface clean and consistent, this project uses a set of reusable UI components. These components handle common UI patterns like buttons, inputs, avatars, and scroll areas.&lt;/p&gt;

&lt;p&gt;Instead of building these from scratch, the project uses &lt;strong&gt;shadcn ui&lt;/strong&gt;, which is a collection of accessible, unstyled components built on top of Radix UI. This approach gives us flexibility while keeping the UI consistent.&lt;/p&gt;

&lt;p&gt;All reusable UI components live inside the &lt;code&gt;components/ui&lt;/code&gt; folder.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusable UI primitives&lt;/strong&gt;: Shared building blocks like buttons, inputs, avatars, and dropdowns used across the app to keep styling consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built with shadcn ui (powered by Radix UI)&lt;/strong&gt;: Accessible, unstyled components that provide behavior and keyboard support without locking the design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scroll handling for chat&lt;/strong&gt;: The &lt;code&gt;ScrollArea&lt;/code&gt; component manages smooth scrolling for long message lists in the chat view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of concerns&lt;/strong&gt;: UI components handle appearance and interaction, while chat and layout components focus on structure and logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to extend and customize&lt;/strong&gt;: Updating a UI component in this folder automatically updates its usage across the entire app.&lt;/li&gt;
&lt;/ul&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%2Fcv7m3tkii8oqsvnj9w82.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%2Fcv7m3tkii8oqsvnj9w82.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;User Management for Collaboration Testing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To test real-time collaboration features like presence, comments, and notifications, we need a way to simulate multiple users. Instead of setting up a full authentication system, this project uses a simple and beginner-friendly approach with predefined users.&lt;/p&gt;

&lt;p&gt;User state is managed using &lt;a href="https://zustand.docs.pmnd.rs/" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt;, a lightweight state management library.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&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="s2"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;persist&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="s2"&gt;zustand/middleware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;uid&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="nl"&gt;displayName&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="nl"&gt;email&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="nl"&gt;photoUrl&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="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&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="nl"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user002&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nany&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUserStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserStore&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;
  &lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-storage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Theme Management with &lt;code&gt;useTheme&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Modern apps usually support both light and dark themes, and this project follows the same pattern. Theme state is managed using a custom React hook, which keeps the logic reusable and easy to maintain.&lt;/p&gt;

&lt;p&gt;The theme logic lives inside a single file.&lt;/p&gt;

&lt;p&gt;The theme is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stored in React state&lt;/li&gt;
&lt;li&gt;Loaded from &lt;code&gt;localStorage&lt;/code&gt; on page load&lt;/li&gt;
&lt;li&gt;Saved back to &lt;code&gt;localStorage&lt;/code&gt; when changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the user’s theme preference persists across page refreshes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syncing Theme with Velt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The theme value is also passed to Velt components, allowing them to automatically switch between light and dark modes.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app UI and Velt UI stay visually consistent&lt;/li&gt;
&lt;li&gt;No extra styling logic is required for collaboration components&lt;/li&gt;
&lt;li&gt;Once the hook is set up, theme changes propagate everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Utility Functions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;lib&lt;/code&gt; folder contains small helper utilities that are shared across the application. In this project, it mainly exists to support reusable UI components and keep common logic out of individual files. The utilities in this file are primarily related to class name handling. When working with Tailwind CSS and reusable components, it’s common to conditionally apply multiple class names.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running the App and Testing the Output&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we’ve walked through the project structure and key files, it’s time to run the app and verify that everything works as expected.&lt;/p&gt;

&lt;p&gt;Seeing the app in action makes the collaboration features easier to understand in the next sections.&lt;/p&gt;

&lt;p&gt;From the project root, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the server starts, open your browser and navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a Zulip style chat interface with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A sidebar for channels&lt;/li&gt;
&lt;li&gt;A header with user controls&lt;/li&gt;
&lt;li&gt;A central chat area for messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test collaboration features, open the app in two browser windows or one normal window and one incognito window.&lt;/p&gt;

&lt;p&gt;Use the user switcher in the header to change between demo users such as Nany and Mary.&lt;/p&gt;

&lt;p&gt;Each window should represent a different user.&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%2F08w6t0lx9imn4xpn2j6u.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%2F08w6t0lx9imn4xpn2j6u.png" alt="Chat Window" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Presence:&lt;/strong&gt; Notice user avatars appear in the header when multiple users are online.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments:&lt;/strong&gt; Hover over a message and add a comment. The comment should appear instantly in both windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications:&lt;/strong&gt; Add a comment in one window and check the notification bell in the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme Switching:&lt;/strong&gt; Toggle between light and dark mode and observe that both the app and Velt components update correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How It Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s a simple view of what happens when the app runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app loads and initializes Velt using the API key in the layout&lt;/li&gt;
&lt;li&gt;A demo user is selected using the Zustand store&lt;/li&gt;
&lt;li&gt;Velt identifies the active user and associates them with the app&lt;/li&gt;
&lt;li&gt;A shared document context is set for collaboration&lt;/li&gt;
&lt;li&gt;Messages render in the chat area&lt;/li&gt;
&lt;li&gt;Comments, presence, and notifications are automatically tracked by Velt&lt;/li&gt;
&lt;li&gt;Updates sync instantly across all connected clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key idea is that the app focuses on UI and user flow, while Velt handles real-time synchronization and collaboration logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Collaborative Features You Get Automatically&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once Velt is integrated, collaboration features start working without writing additional backend or real-time code.&lt;/p&gt;

&lt;p&gt;These features are available out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inline comments:&lt;/strong&gt; Add comments directly on chat messages and view them in context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User presence:&lt;/strong&gt; See who is currently online and active in the chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications:&lt;/strong&gt; Get notified when someone comments or interacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactions and comment status:&lt;/strong&gt; React to comments and mark them as resolved or active.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read awareness:&lt;/strong&gt; See when comments have been viewed by other users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these features work automatically once Velt is set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Things to Note Before Shipping to Production&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before using this setup in a real product, keep the following in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace demo users with a real authentication system&lt;/li&gt;
&lt;li&gt;Identify users using real user IDs and metadata&lt;/li&gt;
&lt;li&gt;Use dynamic document IDs instead of a hardcoded value&lt;/li&gt;
&lt;li&gt;Add permissions to control who can view or comment&lt;/li&gt;
&lt;li&gt;Handle error states when collaboration services are unavailable&lt;/li&gt;
&lt;li&gt;Review Velt customization options to match your product UI&lt;/li&gt;
&lt;li&gt;Test performance with multiple concurrent users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps ensure the app scales safely beyond a demo environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Demo Video&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can see the completed Zulip style chat app with real-time collaboration in action here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zulip-velt-chat.vercel.app/" rel="noopener noreferrer"&gt;https://zulip-velt-chat.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ppyzsybdw2w" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This demo shows user switching, comments, presence, notifications, and theme support working together.&lt;/p&gt;

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

&lt;p&gt;Zulip demonstrates how powerful chat can be when collaboration is built into the experience. Organized conversations, clear context, and real-time awareness turn chat from simple messaging into a system teams can rely on.&lt;/p&gt;

&lt;p&gt;For builders, the real challenge is not the UI. It is the collaboration layer behind it. Features like real-time sync, presence tracking, inline comments, notifications, and reactions usually require complex infrastructure and long development cycles. This is especially true when building a chat-based SaaS product.&lt;/p&gt;

&lt;p&gt;In this tutorial, we focused on that exact problem. Instead of rebuilding collaboration from scratch, we showed how to add Zulip-style collaborative features inside a chat application using Next.js and a modern frontend stack, while delegating the hard real-time problems to Velt.&lt;/p&gt;

&lt;p&gt;The result is a working chat app where collaboration feels native, but the code stays simple and frontend-focused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean component structure makes complex apps easier to understand&lt;/li&gt;
&lt;li&gt;Collaboration does not need custom real-time infrastructure&lt;/li&gt;
&lt;li&gt;Velt enables comments, presence, notifications, and more out of the box&lt;/li&gt;
&lt;li&gt;Frontend-focused development speeds up experimentation and iteration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building a chat app, internal tool, or collaborative product, Velt lets you focus on your core experience instead of infrastructure.&lt;/p&gt;

&lt;p&gt;Try building with &lt;a href="https://velt.dev/" rel="noopener noreferrer"&gt;Velt&lt;/a&gt; and add collaboration to your app in minutes at velt.dev.&lt;/p&gt;

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