<?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: Trailguide</title>
    <description>The latest articles on DEV Community by Trailguide (@trailguide).</description>
    <link>https://dev.to/trailguide</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3802868%2Fa3bc0a0e-7613-4b51-bef7-250aed802b47.png</url>
      <title>DEV Community: Trailguide</title>
      <link>https://dev.to/trailguide</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trailguide"/>
    <language>en</language>
    <item>
      <title>Your product tour ran. Did anyone finish it?</title>
      <dc:creator>Trailguide</dc:creator>
      <pubDate>Fri, 01 May 2026 14:36:55 +0000</pubDate>
      <link>https://dev.to/trailguide/your-product-tour-ran-did-anyone-finish-it-53d0</link>
      <guid>https://dev.to/trailguide/your-product-tour-ran-did-anyone-finish-it-53d0</guid>
      <description>&lt;p&gt;The first article I wrote about Trailguide was about why tours should live in your repo as JSON files. The engineering case: version control, CI testing, no vendor lock-in.&lt;br&gt;&lt;br&gt;
                                                                                                                                                          This one is about what happens after you ship a tour.&lt;br&gt;
                                                                                                                                                                             The part most teams skip                                                                                                                                     You add an onboarding tour. Users start seeing it. You move on to the next feature. Three months later someone asks "is that tour still working?" and nobody actually knows.&lt;br&gt;
                                                                                                                                                            That is the default state for most teams. You built the tour, you have no data on it, and the only signal you get is the absence of complaints.            &lt;/p&gt;

&lt;p&gt;What you actually want to know is pretty simple: how many people start the tour, how many finish it, and which step is where most of them leave.           &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the analytics actually show&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
                                                                                                                                                           Trailguide Pro tracks this without any setup. No events to wire up, no analytics SDK to configure. When you open the dashboard for a trail you see a completion funnel. Each step has a bar showing what percentage of sessions made it that far, color coded by health.&lt;/p&gt;

&lt;p&gt;Most teams find the same pattern. One step causes 60 to 70 percent of abandonment. It's usually either too long, asks the user to do something before they're ready, or it fires on an element that loads slowly so the tooltip appears before the user can even read what's on the page.&lt;br&gt;
                                                                                                                                                             &lt;strong&gt;Once you can see it you fix it. Before that you're guessing.&lt;/strong&gt;                                                                                               &lt;/p&gt;

&lt;p&gt;There's also a chart showing starts versus completions over time, so when you ship a fix you can actually see whether it moved the number. It compares the current period to the prior one. The loop is tight: change something, wait a few days, see if it helped.                                                 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hearing from users directly&lt;/strong&gt; (this is most important)                                                                                                                             &lt;/p&gt;

&lt;p&gt;The other useful thing is getting feedback at the right moment in the flow.                                                                                &lt;/p&gt;

&lt;p&gt;Trailguide has a feedback step type. You drop it anywhere in a tour, after a tricky step or before the last one, and it shows a short prompt. Rating, freeform comment, or both. The responses go into Product Signals which does automatic sentiment scoring and pulls word themes out of the free text.&lt;br&gt;&lt;br&gt;
                                                                                                                                                             You end up with a ranked list of the words users reach for when they're confused or stuck. That's a different signal than a completion rate. One tells you where people leave. The other tells you why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The full picture&lt;/strong&gt;                                                                                                                                         &lt;/p&gt;

&lt;p&gt;The first article ended with "one file, two jobs." The same JSON that shows users an onboarding tour also runs as a Playwright regression test in CI.      &lt;/p&gt;

&lt;p&gt;The fuller picture is that you build the tour, users walk through it, CI catches it if something breaks, and the analytics and feedback tell you what to fix next. Then you update the JSON and ship again.&lt;br&gt;&lt;br&gt;
                                                                                                                                                             Nothing about that requires a vendor dashboard or a third party owning your content. The tours are files in your repo. The test runs in your CI pipeline. The analytics live in the Pro dashboard but they point back to a problem you fix in code.&lt;/p&gt;

&lt;p&gt;That's the thing that took me a while to figure out how to explain. It's not really a tour tool. &lt;strong&gt;It's the whole cycle&lt;/strong&gt;.                                   &lt;/p&gt;

&lt;p&gt;Free runtime at github.com/hellotrailguide/trailguide or try the Pro Editor free for 14 days at gettrailguide.com.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why Product Tours Should Live in Your Git Repo</title>
      <dc:creator>Trailguide</dc:creator>
      <pubDate>Tue, 03 Mar 2026 13:04:27 +0000</pubDate>
      <link>https://dev.to/trailguide/why-product-tours-should-live-in-your-git-repo-1ipd</link>
      <guid>https://dev.to/trailguide/why-product-tours-should-live-in-your-git-repo-1ipd</guid>
      <description>&lt;h1&gt;
  
  
  Why Product Tours Should Live in Your Git Repo
&lt;/h1&gt;

&lt;p&gt;Most product tour tools have the same problem: they charge per user, lock your content in their dashboard, and have no answer for what happens when your UI changes and the tour breaks. You find out the same way your users do.&lt;/p&gt;

&lt;p&gt;Trailguide is a free open-source runtime where tours are JSON files that live in your repo. No per-user pricing. No vendor lock-in. You own the content forever.&lt;/p&gt;

&lt;p&gt;This post is about what you actually get when your tours live alongside your code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tours are just JSON files in your repo
&lt;/h2&gt;

&lt;p&gt;A Trailguide trail looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome Tour"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"step-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[data-trail-id='create-btn']"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"placement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bottom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create your first project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click here to get started."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That file lives in your repo. It gets reviewed in PRs. It has a git history. You can roll it back, branch it, diff it. Everything your team already does with code applies to tours automatically — no new process, no new tools.&lt;/p&gt;

&lt;p&gt;When a PM wants to update the copy on step three, the change goes through the same review flow as any other edit. When something breaks, git blame tells you when and why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recording a tour takes minutes, not days
&lt;/h2&gt;

&lt;p&gt;The Pro Editor includes a Chrome extension recorder. Enter your app's URL, click Start Recording, and click through the flow you want to document. Every click captures three things automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The element's CSS selector, using the most stable attribute available (&lt;code&gt;data-trail-id&lt;/code&gt;, &lt;code&gt;data-testid&lt;/code&gt;, &lt;code&gt;aria-label&lt;/code&gt;, or a DOM path as a fallback)&lt;/li&gt;
&lt;li&gt;A screenshot of the page at that moment&lt;/li&gt;
&lt;li&gt;The exact element rect, so the editor can highlight precisely what was targeted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selectors are graded Stable, Moderate, or Fragile based on what they target. A &lt;code&gt;data-trail-id&lt;/code&gt; attribute is Stable — it won't break when you restructure the DOM. A fragile path selector shows a warning and ranked suggestions to fix it in one click.&lt;/p&gt;

&lt;p&gt;When your UI ships an update, you open the editor and see the screenshot from when the tour was recorded. The broken element is highlighted. Anyone on the team can spot what changed, update the selector, and push a fix — without filing an engineering ticket or involving a developer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two-way Git sync means tours live where your code lives
&lt;/h2&gt;

&lt;p&gt;The Pro Editor connects to GitHub and GitLab. You can load any trail file from a repo, edit it in the dashboard, and push it back as a commit or open a PR for review. The dashboard is for editing. The repo is the source of truth.&lt;/p&gt;

&lt;p&gt;This means your tours go through the same deploy process as everything else. A tour change that hasn't been reviewed doesn't go to production any more than a code change would.&lt;/p&gt;




&lt;h2&gt;
  
  
  Analytics without instrumentation
&lt;/h2&gt;

&lt;p&gt;The runtime tracks completion rates, step-by-step drop-off, and time per step automatically. No events to wire up. No analytics SDK to configure. When a step is losing people, you see it in the dashboard.&lt;/p&gt;

&lt;p&gt;Most teams discover that one step causes 60% of tour abandonment. Once you can see that, you fix it. Without data, you're guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  One trail, two purposes
&lt;/h2&gt;

&lt;p&gt;Here's the part that feels like a bonus once you already have everything above.&lt;/p&gt;

&lt;p&gt;Set a trail's &lt;code&gt;mode&lt;/code&gt; to &lt;code&gt;"both"&lt;/code&gt; and the same JSON that guides your users also runs as a Playwright test in CI. Add &lt;code&gt;action&lt;/code&gt; and &lt;code&gt;assert&lt;/code&gt; fields to steps where you want Playwright to verify something:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"step-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[data-trail-id='create-btn']"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bottom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create your first project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click here to get started."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assert"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visible"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then write one test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runTrail&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;@trailguide/playwright&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;welcomeTrail&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;./tours/welcome.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;welcome tour&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runTrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;welcomeTrail&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;runTrail&lt;/code&gt; walks every step in order. If a selector is missing, an action fails, or an assertion is wrong, the test fails and the deploy is blocked. Steps without &lt;code&gt;action&lt;/code&gt; or &lt;code&gt;assert&lt;/code&gt; still run — Trailguide checks that the target element is present, which catches most selector regressions on its own.&lt;/p&gt;

&lt;p&gt;You didn't write a test suite. You wrote a tour. The test is a side effect of having your onboarding in a format that CI can understand.&lt;/p&gt;

&lt;p&gt;Most teams think of product tours and regression tests as two completely separate things — different tools, different formats, different maintenance burden. When tours are JSON files in your repo, that separation disappears. The content you wrote to teach users how to use your product is exactly what you need to verify it still works.&lt;/p&gt;

&lt;p&gt;One file. Two jobs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;The runtime is free and open source:&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; @trailguide/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pro Editor handles recording, Git sync (GitHub and GitLab), screenshot storage, selector quality grades, analytics, and the mode toggle — 14-day free trial, $49/month after. Every trail you create is a file in your repo that your team owns.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://gettrailguide.com" rel="noopener noreferrer"&gt;gettrailguide.com&lt;/a&gt;&lt;br&gt;
or&lt;br&gt;
Source and full docs at &lt;a href="https://github.com/hellotrailguide/trailguide" rel="noopener noreferrer"&gt;github.com/hellotrailguide/trailguide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>javascript</category>
      <category>playwright</category>
    </item>
  </channel>
</rss>
