<?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: Obinna Ekwuno</title>
    <description>The latest articles on DEV Community by Obinna Ekwuno (@obinnaspeaks).</description>
    <link>https://dev.to/obinnaspeaks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F126837%2F506206b9-e8b5-4d46-8ca6-a0ee3b49f94a.jpeg</url>
      <title>DEV Community: Obinna Ekwuno</title>
      <link>https://dev.to/obinnaspeaks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/obinnaspeaks"/>
    <language>en</language>
    <item>
      <title>Your Workday orchestration in VS Code, drawn the way the builder draws it</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Wed, 30 Sep 2026 14:05:22 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/your-workday-orchestration-in-vs-code-drawn-the-way-the-builder-draws-it-4loh</link>
      <guid>https://dev.to/obinnaspeaks/your-workday-orchestration-in-vs-code-drawn-the-way-the-builder-draws-it-4loh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;NOTE: This is a read-only viewer. It renders exported files for reference and clarity. Orchestration Builder remains the source of truth for what will or won't validate or compile the viewer never edits anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Workday Orchestration Builder is a browser-based, low-code drag-and-drop tool on the &lt;a href="https://developer.workday.com/" rel="noopener noreferrer"&gt;Workday Developer Site&lt;/a&gt; used to create real-time, event-driven workflows and integrations. You get a trigger at the top, steps flowing downward, branches fanning out, loops with their bodies tucked inside, and the whole thing validates and compiles as you go. Then you export the project from your app, open the folder in VS Code, click any &lt;code&gt;.orchestration&lt;/code&gt; file, and all of that visual clarity collapses into one line. &lt;/p&gt;

&lt;p&gt;Not one object —one line —thousands of characters long, every value wrapped in a &lt;code&gt;_type&lt;/code&gt; and &lt;code&gt;_value&lt;/code&gt; envelope, and your editor dutifully renders it as an infinite horizontal scroll. You know there is a flow in there. You built it. You just cannot see it from here.&lt;/p&gt;

&lt;p&gt;Workday already bridges part of this with Local Disk Sync, which saves your app to a local folder and keeps the builder and disk in step. That gets the files into git. It doesn't help when you're reading a colleague's branch, reviewing a pull request, or working somewhere Chrome isn't open.&lt;/p&gt;

&lt;p&gt;The builder is where a flow gets made, and the repo is where it gets reviewed, versioned and shared, and I wanted the second place to be as readable as the first. &lt;/p&gt;

&lt;p&gt;So I built a small extension that carries the builder's picture of a flow into the editor. It is called &lt;a href="https://marketplace.visualstudio.com/items?itemName=ekwuno.orchestrate-flow-viewer" rel="noopener noreferrer"&gt;Orchestrate Flow Viewer&lt;/a&gt;, it works in VS Code and Cursor, and it renders &lt;code&gt;.orchestration&lt;/code&gt; and &lt;code&gt;.suborchestration&lt;/code&gt; files as interactive flow diagrams drawn the way the builder draws them. &lt;/p&gt;

&lt;p&gt;This post is the &lt;em&gt;what&lt;/em&gt;, the &lt;em&gt;why&lt;/em&gt;, and enough of the &lt;em&gt;how&lt;/em&gt; that you could build something similar yourself.&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%2F4p1tp6x7or6ada1666ym.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%2F4p1tp6x7or6ada1666ym.png" alt="an .orchestration file open in VS Code showing the diagram view" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Any Workday app can be exported so a developer can see its underlying folder structure. Choose any app that has an orchestration in it, export it, and open it up using this tool (any app with &lt;code&gt;appManifest.json&lt;/code&gt; and an &lt;code&gt;orchestration/&lt;/code&gt; directory). &lt;/p&gt;

&lt;p&gt;Click a flow file, and you get the same picture the builder would show you, right there in your editor. The &lt;strong&gt;Start&lt;/strong&gt; or trigger step sits at the top and the steps flow downward along a spine. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branch on Conditions&lt;/strong&gt; arms fan out side by side, in the same left to right order the builder evaluates them under little white pills and reconverges at a join. When it comes to &lt;strong&gt;Loop&lt;/strong&gt; bodies, they are nested under their loop card behind a chevron you can collapse. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt; handlers are the one place I deliberately depart from the builder. There, local and global handlers open on their own canvas. A static diagram can't click through, so the viewer pulls them inline: the global handler as a detached region below the main flow, and local handlers as a badge on the step that owns them, borrowing the builder's lightning bolt icon.&lt;/p&gt;

&lt;p&gt;If you can read a flow in Orchestration Builder, you can read it here, because the visual language is the same on purpose.&lt;/p&gt;

&lt;p&gt;Selecting any step opens a details panel loaded with context straight from the builder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Step Context: Review assignments, expressions, branch conditions, loop iterators, add integration message severity, and store-document settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resource Resolution: Inspect CSV columns resolved directly from the flow's resources alongside suborchestration parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Subflow Navigation: Click Call Subflow cards to instantly jump to target suborchestrations across your workspace without waiting for page loads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developer Environment Tools: Enjoy full editor support with pan, zoom, a minimap, live re-rendering on edit, theme matching, and a toggle for pretty-printed raw JSON.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why a viewer, and why read-only
&lt;/h2&gt;

&lt;p&gt;Orchestration Builder remains the place to create orchestrations—this project doesn't change that. But once you download the source, you're dealing with files. And files belong where developers already work: in repositories, branches, and pull requests ready for review.&lt;/p&gt;

&lt;p&gt;As Workday expands its developer platform, it brings an expectation that you can work in standard development environments. You build in Orchestrate, then review, version, and share in VS Code or GitHub just like any other code you ship. &lt;/p&gt;

&lt;p&gt;However, the file reads like it was written for the compiler, not for you. The IntelliJ plugin takes the same view, treating the orchestration folder as read-only and pointing you back to Orchestration Builder to edit. The viewer follows that convention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal is intentionally focused: make exported files readable right where they live—nothing more.&lt;/strong&gt; It isn't a secondary editor or a replacement for the builder. The viewer simply renders what is in the file, which doesn't guarantee the flow will validate or compile.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the file knows, and what the builder knows
&lt;/h2&gt;

&lt;p&gt;To reconstruct the builder's visual layout from an exported file, I first examined the underlying file structure. Two key characteristics stood out immediately. First, every single value is wrapped inside a type envelope, as shown when pretty-printing a flow:&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;"_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;"CreateValues"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_value"&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;"name"&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;"String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InputFile"&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;"isDisabled"&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;"Boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;"errorHandler"&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="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Opt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ErrorHandler"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"_value"&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;"ErrorHandler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_value"&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;"…"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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="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;Taking &lt;code&gt;errorHandler&lt;/code&gt; as an example, it is represented as an &lt;code&gt;["Opt", "ErrorHandler"]&lt;/code&gt; wrapper enclosing another inner wrapper. Optional fields effectively create nested envelopes, where an absent optional evaluates to an envelope wrapping null. Furthermore, certain exporter versions emit un-wrapped bare objects for group bodies, requiring the parser to handle both representation styles seamlessly.&lt;/p&gt;

&lt;p&gt;The second, more subtle realization influenced the entire architectural approach: diagram information exists within the Orchestration Builder interface rather than the source file itself. The exported file stores structural data rather than graphical layout—omitting explicit edges, node identifiers, or spatial coordinates. Execution sequence is inferred from array ordering, branching is established through element containment, and loop bodies are structured as nested lists. While this compact format is optimized for platform execution and compilation, re-creating the builder's interactive diagram within VS Code required translating these structural semantics back into a visual node graph.&lt;/p&gt;

&lt;p&gt;This led to a multi-stage pipeline architecture designed to make implicit relationships explicit, enforcing a clear separation of concerns at each phase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.orchestration file (text)
        │
        ▼
  src/core/parse.ts      unwrap the envelopes → typed FlowModel
        │
        ▼
  src/core/graph.ts      synthesise nodes and edges → FlowGraph
        │
        ▼
  src/core/layout.ts     assign x/y/w/h to every node
        │
        ▼
  src/webview/…          render with React Flow inside VS Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All components in &lt;code&gt;src/core/&lt;/code&gt; consist of pure TypeScript without any VS Code API dependencies. Consequently, the core pipeline can execute in standalone Vitest suites or standard browser environments. The VS Code extension itself acts as a lightweight wrapper around this core logic—a design boundary that significantly simplified testing and maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Teach the parser the envelope, once
&lt;/h2&gt;

&lt;p&gt;Before building the parser, I established a clear vocabulary for interpreting the format: eight utility functions in &lt;code&gt;src/core/unwrap.ts&lt;/code&gt; designed to handle any output variation from the exporter. The core work is handled by peel:&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="cm"&gt;/**
 * Peel wrapper-of-wrapper layers (e.g. Opt&amp;lt;ErrorHandler&amp;gt; whose _value is the
 * wrapped ErrorHandler) down to the innermost wrapper. A null Opt stays put,
 * so callers see unwrap(peel(v)) === null for absent optionals.
 */&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;peel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&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="nf"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&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="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cur&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cur&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;Building on this foundation are &lt;code&gt;field&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;num&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, and &lt;code&gt;record&lt;/code&gt;. Each helper safely evaluates wrapped or bare objects, returning &lt;code&gt;undefined&lt;/code&gt; or an empty list for unexpected shapes instead of throwing an error. Routing all property access through these utilities encapsulates envelope parsing entirely. This enforces a core architectural rule across the project: malformed input degrades gracefully rather than crashing the application.&lt;/p&gt;

&lt;p&gt;Assume Workday will ship something you have not seen&lt;/p&gt;

&lt;p&gt;Workday releases new Orchestrate components regularly. This means the parser will inevitably encounter step types it hasn't seen before—and it will meet them in real user projects, not just in test fixtures. The &lt;code&gt;default&lt;/code&gt; case of &lt;code&gt;parseStep&lt;/code&gt; gracefully handles these situations using a two-tiered approach:&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;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Unseen component types must never break the diagram. If the payload&lt;/span&gt;
  &lt;span class="c1"&gt;// carries a body of child steps (loop-like components such as Batch&lt;/span&gt;
  &lt;span class="c1"&gt;// Loop / Join Loop), render it as a container so the children survive.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;groupRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groupRaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nodes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;directNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nodes&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;hasGroup&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;directNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unknown container type "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rawType&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) — rendered generically.`&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;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hasGroup&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groupRaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;childPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;warnings&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;directNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;n&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;parseStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;childPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;warnings&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="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;genericContainer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;leafEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nodes&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;warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unknown step type "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rawType&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) — rendered generically.`&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="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;leafEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&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;If a component I haven't written a parser for contains child steps, it is parsed as a generic container. This allows any nested steps to be parsed normally and displayed as functional cards. Any other unrecognized structure becomes a generic step, displaying a raw dump of its fields in the details panel for complete visibility. Both paths increment a warning counter in the toolbar. While an unseen component might lack a custom icon, it will never break the visual diagram.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I wrote 150 lines instead of using dagre
&lt;/h2&gt;

&lt;p&gt;When it came to the layout stage, using an existing graph library like &lt;a href="https://github.com/dagrejs/dagre" rel="noopener noreferrer"&gt;Dagre&lt;/a&gt; or &lt;a href="https://github.com/kieler/elkjs" rel="noopener noreferrer"&gt;ELK&lt;/a&gt; seemed like the obvious choice. However, I decided to build a custom solution instead. General-purpose layout engines tend to produce organic structures that shift dynamically as the graph changes. In contrast, the Orchestration Builder interface relies on a consistent, highly predictable layout: a single vertical spine flanked by neatly aligned branch arms.  &lt;/p&gt;

&lt;p&gt;Branch order matters too,  Orchestrate evaluates Branch on Conditions arms left to right and runs the first that's true, so a layout engine that reorders arms is misreporting execution order. A custom algorithm guarantees this exact structure every time—ensuring identical input always yields identical output.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;layoutGraph&lt;/code&gt; operates using two recursive tree passes. The first pass measures bottom-up, memoizing each subtree's bounding box so branch arms know their required width before placement. The second pass places elements top-down, centering items along the spine and advancing a y-cursor. Branch arms are centered as rows beneath their root node, and joins are placed at the measured bottom so all paths reconverge seamlessly. &lt;/p&gt;

&lt;p&gt;Centralizing node dimensions in a single lookup table makes global visual adjustments simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SIZES&lt;/span&gt;&lt;span class="p"&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="nx"&gt;NodeKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&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;h&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;branchRoot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;globalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;pill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;132&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;expander&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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;Deterministic layout also makes the visual structure fully unit-testable. For instance, a key test suite iterates through every node pair across all test fixtures to verify that no elements overlap:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has no overlapping nodes&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;overlaps&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="s2"&gt;`&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; overlaps &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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a measurement pass undercounts a subtree, overlapping cards immediately fail the test with the precise node IDs responsible. This test caught several real layout bugs during development—an advantage made possible solely by deterministic positioning, which third-party layout engines cannot reliably guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest is plumbing
&lt;/h2&gt;

&lt;p&gt;The webview component renders via React Flow. Since the layout phase computes absolute node positions in advance, the webview primarily maps graph nodes directly to locked React Flow elements—ideal for a read-only viewer. &lt;/p&gt;

&lt;p&gt;Edges use right-angle elbow connectors rather than bezier curves, categorized by function so horizontal segments route exactly where the Orchestration Builder would place them. Furthermore, because all color styles leverage VS Code's CSS variables, light and dark themes work seamlessly out of the box without requiring custom styling code.&lt;/p&gt;

&lt;p&gt;On the extension side, a &lt;code&gt;CustomTextEditorProvider&lt;/code&gt; allows VS Code to handle document state, text editing, and file watching natively, leaving the extension focused entirely on rendering. Communication between the host and webview relies on a lightweight, typed protocol. Incoming edits stream in with debouncing so side-by-side editing won't trigger heavy re-layouts on every keystroke. Additionally, a workspace index scans orchestration IDs, enabling Call Suborchestration cards to display target names and open corresponding files on click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;If you're building a similar extension or tool, here are three core principles worth applying:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identify the core problem first:&lt;/strong&gt; The goal was never just to render raw JSON, but rather to bring Orchestration Builder's visual clarity directly into the editor environment. Defining this goal made designing the processing pipeline straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep core logic decoupled:&lt;/strong&gt; Avoiding VS Code API dependencies in the core pipeline enabled comprehensive unit testing against exported files, allowed fast diagram iteration in a standalone browser harness, and minimized webview complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for future schema changes:&lt;/strong&gt; Implementing a two-tiered fallback mechanism alongside a warning counter ensures that newly released Workday components render gracefully without breaking the layout achieved in about thirty lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start here
&lt;/h2&gt;

&lt;p&gt;Getting started takes only a couple of minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=ekwuno.orchestrate-flow-viewer" rel="noopener noreferrer"&gt;Orchestrate Flow Viewer on VS Code Marketplace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open a downloaded: Select any project folder containing an &lt;code&gt;appManifest.json&lt;/code&gt; and an &lt;code&gt;orchestration/&lt;/code&gt; directory. &lt;/li&gt;
&lt;li&gt;View the flow: Click any flow file and toggle the JSON view once to compare the formatted diagram against raw source data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The source code is available on GitHub under the MIT license at &lt;a href="https://github.com/Ekwuno/workday-orchestrate-viewer" rel="noopener noreferrer"&gt;https://github.com/Ekwuno/workday-orchestrate-viewer&lt;/a&gt; . Upcoming roadmap features include breadcrumb navigation for subflow drill-ins and PNG/SVG export options. If you encounter rendering issues with an exported flow, please open an issue in the repo &lt;/p&gt;

&lt;p&gt;Orchestration Builder has always understood your flow's visual layout now your editor does too. Head to &lt;a href="https://developer.workday.com/about" rel="noopener noreferrer"&gt;developer.workday.com&lt;/a&gt; and join the Workday Developer Program, where you'll find the guides, the community and everything you need to start building.&lt;/p&gt;

</description>
      <category>workday</category>
      <category>api</category>
      <category>vscode</category>
      <category>devrel</category>
    </item>
    <item>
      <title>TIL: How to make a Synced Sandbox from a GitHub repository</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Thu, 23 Feb 2023 15:06:28 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/til-how-to-make-a-synced-sandbox-from-a-github-repository-1jf7</link>
      <guid>https://dev.to/obinnaspeaks/til-how-to-make-a-synced-sandbox-from-a-github-repository-1jf7</guid>
      <description>&lt;p&gt;"Some features work like magic. Some features are just Magic!" - Some human. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F281ltn0s65k8xtpn39x5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F281ltn0s65k8xtpn39x5.gif" alt="Magic gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today I learned about a really helpful feature on CodeSandbox that automatically allows you to create Sandboxes that stay synced with changes you make to your GitHub repositories. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIL means Today I learned. 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Synced sandboxes
&lt;/h2&gt;

&lt;p&gt;From my understanding, Synced sandboxes are sandboxes that stay up to date with the latest changes you have pushed to a GitHub project using your &lt;code&gt;.git&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Their main advantage is that they allow you to create sandboxes from specific sub-folders within your GitHub projects without importing the whole repo. &lt;/p&gt;

&lt;h3&gt;
  
  
  Make a Synced Sandbox in CodeSandbox
&lt;/h3&gt;

&lt;p&gt;First, open your project on GitHub and then edit the URL to start with &lt;code&gt;codesandbox.io/p/sandbox/&lt;/code&gt; prefix before the &lt;code&gt;github.com&lt;/code&gt; the remove &lt;code&gt;.com&lt;/code&gt; from &lt;code&gt;github.com&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;For example, let's use the demo project from my last blog post on &lt;a href="https://dev.to/obinnaspeaks/how-to-use-content-collection-in-astro-43j2"&gt;Content collection in Astro&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The GitHub URL: &lt;a href="https://github.com/Ekwuno/content-collecttion-api-example" rel="noopener noreferrer"&gt;https://github.com/Ekwuno/content-collecttion-api-example&lt;/a&gt; will become &lt;br&gt;
&lt;a href="https://codesandbox.io/p/sandbox/github/Ekwuno/content-collecttion-api-example" rel="noopener noreferrer"&gt;https://codesandbox.io/p/sandbox/github/Ekwuno/content-collecttion-api-example&lt;/a&gt; &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why does this need to exist?
&lt;/h2&gt;

&lt;p&gt;When changes are made to your GitHub projects or your example subfolders, you have to do the stressful task of updating all other replicas across platforms, but with synced sandboxes, they all stay up to date because they are "synced" to the source.&lt;/p&gt;

&lt;p&gt;Once you push a change to GitHub, your sandbox also has those changes.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Use case for Open source projects.
&lt;/h3&gt;

&lt;p&gt;Another use case I see is for Open-source projects that have example folders. Most frameworks have a repo with example folders which are usually sub-folders that get harder to maintain. By making these example folders synced sandboxes, each update is added automatically, allowing maintainers or contributors to share specific folders as sandboxes so they can be viewed as individual projects. &lt;/p&gt;

&lt;h3&gt;
  
  
  Use case for Embedding Cloud sandbox to docs.
&lt;/h3&gt;

&lt;p&gt;The final use case I  see for this feature is embedding your GitHub repository in the documentation and ensuring it is always up to date. So when people come to docs, they can create sandboxes from the embeds.  &lt;/p&gt;

&lt;p&gt;I recently made one of these for a project &lt;a href="https://twitter.com/jsjoeio" rel="noopener noreferrer"&gt;Joe Previte&lt;/a&gt; made and now whenever Joe makes an update, this sandbox I made gets the changes. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1627819277267894272-435" src="https://platform.twitter.com/embed/Tweet.html?id=1627819277267894272"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1627819277267894272-435');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1627819277267894272&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;That's all, folks!  &lt;/p&gt;

</description>
      <category>codesandbox</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to use content collection in Astro.</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Mon, 06 Feb 2023 16:38:50 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/how-to-use-content-collection-in-astro-43j2</link>
      <guid>https://dev.to/obinnaspeaks/how-to-use-content-collection-in-astro-43j2</guid>
      <description>&lt;p&gt;In case you missed it, Astro launched 2.0 with a couple of exciting announcements, one of which is the new &lt;a href="https://docs.astro.build/en/guides/content-collections/" rel="noopener noreferrer"&gt;Content collection API&lt;/a&gt; with Type-safety powered by &lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In a previous post, I wrote a summary of &lt;a href="https://dev.to/obinnaspeaks/whats-new-in-astro-20-m42"&gt;What's new in Astro 2.0&lt;/a&gt;; you can check it out for more information.&lt;/p&gt;

&lt;p&gt;This post will look at creating Content collections and how you can start building and sourcing content in your Astro application.&lt;/p&gt;

&lt;p&gt;We will be doing so by building a simple newsletter application, exploring creating a collection, querying the collection in components and creating routes for each markdown file in the collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;To follow the project in the blogpost, you can fork my &lt;a href="https://codesandbox.io/p/github/Ekwuno/content-collecttion-api-example/main?file=%2FREADME.md" rel="noopener noreferrer"&gt;content collection example project&lt;/a&gt; on Codesandbox. Create a fork, and you are good to go.&lt;/p&gt;

&lt;p&gt;Pull the repo from &lt;a href="https://github.com/Ekwuno/content-collecttion-api-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and set up if you prefer to do this locally. &lt;/p&gt;

&lt;p&gt;The parts that are covered is the &lt;code&gt;Content/&lt;/code&gt;, &lt;code&gt;Pages/newsletter&lt;/code&gt; folders. The file structure looks like and t&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/
├── public/
│   └── favicon.svg
├── src/
│   ├── components/
│   │   └── Card.astro
│   ├── content/
│   │   └── newsletter
│   │       ├── post-1.md
│   │       └── post-2.md
|   ├── config.ts
│   ├── layouts/
│   │   └── Layout.astro
│   └── pages/
│       ├── newsletter
│       │   ├── [slug].astro
│       └── index.astro
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up  Content Collections in Astro
&lt;/h2&gt;

&lt;p&gt;A content collection is a group of &lt;code&gt;.mdx&lt;/code&gt; or &lt;code&gt;.md&lt;/code&gt; files that are created under the &lt;code&gt;src/content&lt;/code&gt; folder in Astro. The  content files are type-safe and allow you to group your content in a schema and type-safe way. Once you have a collection, you can start querying your content using Astro’s built-in Content APIs. &lt;/p&gt;

&lt;h3&gt;
  
  
  Define collection schema
&lt;/h3&gt;

&lt;p&gt;First, create a &lt;code&gt;config.ts&lt;/code&gt; file in the &lt;code&gt;src/content&lt;/code&gt; folder, which is the config file where you define the "collections" for your project. We describe the frontmatter schema in this file and use Zod to define types.&lt;/p&gt;

&lt;p&gt;From the code block below, you can notice that we define the schema with some types. Doing so ensures that when we create new &lt;code&gt;.md&lt;/code&gt; or &lt;code&gt;.mdx&lt;/code&gt; files, the frontmatter is type-safe, and we get the benefits of Typescript in markdown.&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;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define a collection of newsletter posts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newsletterCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="c1"&gt;// Define the schema &lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;//Image can be optional&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Export&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//  collectionName: collection&lt;/span&gt;
    &lt;span class="na"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newsletterCollection&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;
  
  
  Creating Markdown content in Astro
&lt;/h3&gt;

&lt;p&gt;Since we are creating content collections, the markdown files must follow a "collection" type format. So in the &lt;code&gt;src/content&lt;/code&gt; folder, create a &lt;code&gt;newsletter&lt;/code&gt; folder this is where we add markdown files and inside those files add the frontmatter, following the schema format that is was defined. &lt;/p&gt;

&lt;p&gt;Under the newsletter folder create a &lt;code&gt;post-1.md&lt;/code&gt; and then define the frontmatter with respect to the schema&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Newsletter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Post&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2019-01-01T00:00:00.000Z&lt;/span&gt;
&lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Newsletter&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;News&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;first&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;newsletter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;post."&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# Newsletter Post 1&lt;/span&gt;

// Content 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The frontmatter is type-safe, so if you add any field that isn't defined in the schema, you will get a prompt to fix the error. Thankfully Astro has very &lt;a href="https://dev.to/obinnaspeaks/whats-new-in-astro-20-m42"&gt;helpful error messages to prompt&lt;/a&gt;. Also, notice how no image is defined because, in the schema, it is marked as optional. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Importing Content collections to components
&lt;/h2&gt;

&lt;p&gt;Now that you have defined the schema and have your first post up. You can pull your content data using the &lt;code&gt;getCollection&lt;/code&gt; from &lt;code&gt;astro:content&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Then pass your collection name into the &lt;code&gt;getCollection&lt;/code&gt; function. Doing so will give you an array that you can map through and display in a component. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might need to restart your dev server after importing so that astro:content can be accessed.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newsletters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;newsletters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newsletter&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Card&lt;/span&gt;
         &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/newsletter/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&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;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the &lt;code&gt;href&lt;/code&gt; for the newsletter cards opens up a path &lt;code&gt;/newsletter/${newsletter.slug}&lt;/code&gt;. This is because, in Astro, all pages are created under the pages folder so, we create a &lt;code&gt;newsletter&lt;/code&gt; folder under pages to make routes for each post. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can make multiple routes for each collection that you have.   &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Generate Pages for Content collections
&lt;/h2&gt;

&lt;p&gt;If you are familiar with creating dynamic routes, they are often depicted with &lt;code&gt;[]&lt;/code&gt; and have an identifier to create the routes based on the identifier. &lt;br&gt;
For example, it can be making each route based on the id:&lt;code&gt;[name.id]&lt;/code&gt; or on the folder's name &lt;code&gt;[Professions.name]&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To create a new page for each of the newsletter entries under the Pages folder, create &lt;code&gt;newsletters/[slug].astro&lt;/code&gt;; this will create a new page for each post. &lt;/p&gt;

&lt;p&gt;Of course, you can also &lt;a href="https://docs.astro.build/en/guides/content-collections/#defining-custom-slugs" rel="noopener noreferrer"&gt;define custom slugs&lt;/a&gt; for your files, but we will stick with the generated ones.&lt;/p&gt;

&lt;p&gt;First, get the collection; you will use the &lt;code&gt;getCollection&lt;/code&gt; function again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, because we want to create paths from these collections at build time, we make a function &lt;code&gt;getStaticPaths&lt;/code&gt;. In this function, we will pass the newsletter collection into &lt;code&gt;getCollection&lt;/code&gt; and await the response. &lt;/p&gt;

&lt;p&gt;This response is then mapped out to create a slug for each post at build time, the params Object shows what file is  rendered, as seen below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// This function gets called at build time and generates the paths from the content folder&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticPaths&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define the colection you are creating pages for&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allNewsletters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newsletter&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="nx"&gt;allNewsletters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newsletter&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
            &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nx"&gt;newsletter&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;h3&gt;
  
  
  Access props and rendering markdown for Content collections.
&lt;/h3&gt;

&lt;p&gt;Now that we have defined the newsletter, we would need to access the props in this particular component, and we can do that with &lt;code&gt;Astro.props&lt;/code&gt; to typecast this even further add a &lt;br&gt;
&lt;code&gt;{CollectionEntry&amp;lt;&amp;gt;}&lt;/code&gt; type and pass in &lt;code&gt;newsletter&lt;/code&gt; as seen below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;CollectionEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//.. &lt;/span&gt;

&lt;span class="c1"&gt;// Access the props in this component &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;newsletter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CollectionEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&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;To get the actual content from the &lt;code&gt;post-1.md&lt;/code&gt; file, call the &lt;code&gt;render&lt;/code&gt; function pass in the Content. &lt;/p&gt;

&lt;p&gt;We can now render the content like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&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;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newsletter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Content&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Back&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt; &lt;span class="nx"&gt;newsletters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;p&gt;Astro's content collection API allows for so flexibility and type-safety when it comes to handling data. There is also a guide for &lt;a href="https://docs.astro.build/en/guides/content-collections/#migrating-from-file-based-routing" rel="noopener noreferrer"&gt;migrating from File-based routing&lt;/a&gt; if you want to update your projects. &lt;/p&gt;

&lt;p&gt;To get started with this example project, you can create a fork of my &lt;a href="https://codesandbox.io/p/github/Ekwuno/content-collecttion-api-example/main?file=%2FREADME.md" rel="noopener noreferrer"&gt;Content-collection API&lt;/a&gt; project on CodeSandbox. Happy coding 👋🏾&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>What's new in Astro 2.0?</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Wed, 25 Jan 2023 16:08:31 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/whats-new-in-astro-20-m42</link>
      <guid>https://dev.to/obinnaspeaks/whats-new-in-astro-20-m42</guid>
      <description>&lt;p&gt;Astro 1.0 introduced developers to a way of building applications optimised to reduce excess Javascript in an application by introducing us to &lt;a href="https://docs.astro.build/en/concepts/islands/"&gt;Astro Islands&lt;/a&gt;, representing a paradigm shift for frontend web architecture.&lt;/p&gt;

&lt;p&gt;This shift allows people to build static Pages and implement "Islands" of dynamic behaviour using frameworks you were already familiar with, like React, Vue, and Preact ... &lt;/p&gt;

&lt;p&gt;This shift brought &lt;strong&gt;ASTRO&lt;/strong&gt;nomical (Pun intended) improvements to the performance of your application as the Unused JavaScript got replaced with lightweight HTML, which meant faster loads and time-to-interactive (TTI).&lt;/p&gt;

&lt;p&gt;The use case of Astro spans E-commerce and blogs. As you can imagine, the list keeps growing. &lt;/p&gt;

&lt;h2&gt;
  
  
  So... Astro 2.0?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc07px1fe8n4umzsbiko.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc07px1fe8n4umzsbiko.gif" alt="Wait there is more" width="498" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Collection API
&lt;/h2&gt;

&lt;p&gt;Since a lot of the content coming into websites these days are static coming from a content management system (CMS) or Markdown or MDX, Astro has added a Collection API which allows you to manage collections of extended markdown files in a Type-safe way! &lt;/p&gt;

&lt;p&gt;If you are familiar with Typescript's data inference, you can now get that in Astro powered by &lt;a href="https://github.com/colinhacks/zod"&gt;zod&lt;/a&gt;, which is a Typescript validation library.The &lt;code&gt;src/content&lt;/code&gt; directory allows you to group together markdown files and access them from within your application. &lt;/p&gt;

&lt;p&gt;Learn more about the &lt;a href="https://docs.astro.build/en/guides/content-collections/"&gt;Content API&lt;/a&gt; in the documentation&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid rendering..
&lt;/h2&gt;

&lt;p&gt;When building applications you often have to make the decision between static (SSG) and server (SSR) build output.&lt;/p&gt;

&lt;p&gt;What if... ? What if you could enable server side rendering and static site generation on a per route bases?? &lt;/p&gt;

&lt;p&gt;Well now you can with &lt;em&gt;Astro 2.0&lt;/em&gt;!! This means Improve render performance of popular pages and performance improvements with builds. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can now pre-render certain pages without giving up your &lt;br&gt;
 deployed server. Pre-rendering builds pages ahead-of-time so &lt;br&gt;
 that all of those requests load instantly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Improved Error messaging
&lt;/h2&gt;

&lt;p&gt;Astro 2.0 provides better error messaging, you know.. the type that is helpful and doesn't make you want to do this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzf0d08sixlm8aqtqep3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzf0d08sixlm8aqtqep3.gif" alt="Developer throwing laptop away" width="306" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's so much better, because it points out exactly what's wrong.  &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1617959618285633536-55" src="https://platform.twitter.com/embed/Tweet.html?id=1617959618285633536"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1617959618285633536-55');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1617959618285633536&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Improvements
&lt;/h3&gt;

&lt;p&gt;There have been improvements to the Hot module reload(HMR) of Astro, and it also now supports some Vite 4.0 improvements. &lt;/p&gt;

&lt;p&gt;Astro also officially launch the &lt;a href="https://github.com/orgs/withastro/projects/11"&gt;new Public Roadmap&lt;/a&gt; for Astro. So now you can see all the things the team is up to! &lt;/p&gt;

&lt;p&gt;You can start testing these new features today by running &lt;code&gt;npm i astro@latest&lt;/code&gt; to upgrade an existing project. If you don't have an existing project, you can start a new project locally or on an Online  development environment like Codesandbox.&lt;/p&gt;

&lt;p&gt;Codesandbox has automatic Language syntax support for Astro and is a really seemless way to build applications without setting things up locally as it is powered by micro VMs &lt;/p&gt;

&lt;p&gt;Check out the Astro starter running 2.0 already:  &lt;a href="https://codesandbox.io/p/sandbox/astro-j1qiqf"&gt;https://codesandbox.io/p/sandbox/astro-j1qiqf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a follow up blog post I'll be sharing a project using Astro 2.0! &lt;/p&gt;

&lt;p&gt;Happy coding! 🎉&lt;/p&gt;

</description>
      <category>astro</category>
    </item>
    <item>
      <title>Auditing for accessibility with Evinced</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Wed, 03 Feb 2021 18:45:39 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/auditing-for-accessibility-with-evinced-4j3a</link>
      <guid>https://dev.to/obinnaspeaks/auditing-for-accessibility-with-evinced-4j3a</guid>
      <description>&lt;p&gt;Again, the topic for today is accessibility. I know sometimes when you hear about this your eyes roll to the back of your head. Maybe. because the process of testing for this can be a bit too long or stressful. &lt;/p&gt;

&lt;p&gt;Another reason might be you don't even know exactly what you are looking for.  Now you have 50 tabs open looking for the answer to a "few" (37 heading to 60) accessibility warnings. &lt;/p&gt;

&lt;p&gt;I am going, to be frank with you, auditing for accessibility defects is hard. Even harder when you don't know if the bug you are fixing exists in one place or does it span your app. -- This is another horror story.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Where does it all end?
&lt;/h2&gt;

&lt;p&gt;I spent a couple of weeks testing out a new site scanner feature on a web service called Evinced. Evinced provides a way to pinpoint accessibility defects in a site by allowing the algorithm to crawl all the pages (or selected pages) on your website and scan for pages that have accessibility debts &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Evinced has multiple tools for auditing accessibility, you can check them out on the &lt;a href="https://www.evinced.com/products"&gt;products page&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The thing I love about this tool's site scanner feature is how it points out the specific problem and also redirects you to a glossary where you can learn more about the error it picked up and even examples. &lt;/p&gt;

&lt;h2&gt;
  
  
  Too much talk show me a demo!
&lt;/h2&gt;

&lt;p&gt;I used this tool to test out some websites that I had been curious to know how they'd perform. The first site I tested was my personal site of course! &lt;/p&gt;

&lt;p&gt;Now my personal site has an accessibility lighthouse score of 84, I set some color-contrast errors to see if Evinced would pick it up. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most of these accessibility errors are in a &lt;a href="https://resume.obinnaspeaks.dev/"&gt;subdomain of my website&lt;/a&gt; so Let's see how this plays out.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7yyel67l7u6xyu2n780a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7yyel67l7u6xyu2n780a.png" alt="acessibility Lighthouse scores for obinnaspeaks.dev" width="800" height="457"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Step 1:  Go to &lt;a href="https://app.evinced.com/"&gt;https://app.evinced.com/&lt;/a&gt;, you will be directed to apply for the free Community Service or Enterprise Free Trial for the Site Scanner, and then log in.&lt;/p&gt;

&lt;p&gt;When that is done you'll see the modal below, then proceed to put your site URL in the property input box &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcm4din87rtwehdru6xbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcm4din87rtwehdru6xbg.png" alt="Input Modal for site URL" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Allow the site scanner to crawl and scan your site and wait for the magic. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi82op5avmuiy12re5x86.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi82op5avmuiy12re5x86.png" alt="Ready modal to confirm if you want to start mapping" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the scan is over, you can see that it has gone through every part of your site (or the pages specified) and picked up all the accessibility errors, and even provides you with a break down of these errors  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrqt3ldva8gg8al6ty0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrqt3ldva8gg8al6ty0j.png" alt="Screenshot of accessibility error breakdown" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this help me fix it?
&lt;/h2&gt;

&lt;p&gt;My 48 counts of violations for my website can be broken down by Evinced into components, that's really helpful because I can actually tell what's going on and where it is going on. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftzn92uu1g9wbzfkofrps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftzn92uu1g9wbzfkofrps.png" alt="Component level guide for accessiblity errors" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this shows me is the exact problem and If I go into developer tools and checked the component I can fix it in chrome and that automatically reflects in my codebase &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4j3wbtpqft63irt32qlm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4j3wbtpqft63irt32qlm.png" alt="Screenshot of fixing color contrast issues using web inspector" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and feedback.
&lt;/h2&gt;

&lt;p&gt;I only tested this on my site because I created the accessibility errors and I wanted to see if the tool would pick it up. &lt;/p&gt;

&lt;p&gt;However, a progress bar for the crawling and scanning process would be great I wasn’t sure when these processes finished because I just left the site and did something else. &lt;/p&gt;

&lt;p&gt;At the end of the day, I am happy that more tools that aid and automate the process of accessibility exist. &lt;/p&gt;

</description>
      <category>a11y</category>
    </item>
    <item>
      <title>Gatsby Cloud and Gatsby open source aren't mutually exclusive. Here's why I think so.</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Mon, 02 Nov 2020 21:54:13 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/gatsby-cloud-and-gatsby-open-source-aren-t-mutually-exclusive-here-s-why-i-think-so-a1g</link>
      <guid>https://dev.to/obinnaspeaks/gatsby-cloud-and-gatsby-open-source-aren-t-mutually-exclusive-here-s-why-i-think-so-a1g</guid>
      <description>&lt;p&gt;Now I know, I work at Gatsby. I am supposed to say "good" things about Gatsby -- but here's the thing. Before anything, I am a developer and I care about my community. &lt;/p&gt;

&lt;p&gt;I have spent a couple of days asking a lot of questions about how our community views the Cloud offering of Gatsby. Most of the comments seem to view it as Gatsby's "money maker", &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjot4sudt8ireevw8bfy5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjot4sudt8ireevw8bfy5.jpg" alt="Well yes, but then no."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being open-source while also making a profit as a business seems to be a conflict in some minds. When open source starts to get involved with money, if not put in the proper context it can be seen as !Open-source (Not open-source, pardon me I couldn't help it. ) &lt;/p&gt;

&lt;h2&gt;
  
  
  Here's what it actually is.
&lt;/h2&gt;

&lt;p&gt;Gatsby Cloud, simply put, is an optimization tool for sites Gatsby built with the open-source framework. It will improve how fast your Gatsby sites are built and also offers you more optimizations as your sites grow. &lt;/p&gt;

&lt;p&gt;It's like playing God of War on a PS4, for example. In the beginning, you have only a simple axe.  But as you go further into the game you unlock pretty awesome abilities -- and you pay for them with the coins you have collected along the way. (Yes! I am currently playing God of War 😅). &lt;/p&gt;

&lt;p&gt;Gatsby was designed for front end devs to be able to assemble websites from modular services, each one having its own best-fit data source, using plugins to make it quick to build this way. This architecture means that a site built with Gatsby is fast anywhere it runs. But for a Gatsby site to be as fast as it is architected to be there needed to be customized cloud infrastructure. &lt;/p&gt;

&lt;p&gt;My point is Gatsby Cloud wasn't built to just "make money for Gatsby" -- although users paying for the advanced features added to it will help pay the Engineers working full time on open source Gatsby (i.e me and my team). It was built as the arena for Gatsby sites to reach their true superpowers.&lt;/p&gt;

&lt;p&gt;Gatsby Cloud is of course a cloud platform. This infrastructure costs money! But we support open source even in our Cloud platform: we will always have a free tier for personal projects. Because we want anybody using the open-source Gatsby framework to be able to experience the ideal place to build and deploy their projects. If you're a company using a lot of Cloud builds on a product you make money from well, then, of course, we would like for you to pay for your usage.&lt;/p&gt;

&lt;p&gt;Now, with that in mind here's what you get from using Gatsby Cloud to build your Gatsby Sites for free:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 Real-Time Edits/month&lt;/li&gt;
&lt;li&gt;Standard Builds&lt;/li&gt;
&lt;li&gt;Deploy Previews&lt;/li&gt;
&lt;li&gt;Real-time CMS Previews&lt;/li&gt;
&lt;li&gt;Lighthouse Reports&lt;/li&gt;
&lt;li&gt;1 editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frp3zamhxinnrsd8ggtf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frp3zamhxinnrsd8ggtf8.png" alt="Gatsby cloud plans"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, this is pretty helpful for a free plan and if you're not running a really giant site or doing constant updating this is perfect. &lt;/p&gt;

&lt;p&gt;Now the more advanced stuff like &lt;a href="https://blog.logrocket.com/gatsby-incremental-builds/" rel="noopener noreferrer"&gt;Incremental build&lt;/a&gt;, Site Access Control, extended real-time edits/month for larger themes, comes with the paid plans (which start at $19/Month 🤭). This is cool! But tbh these are things that big teams working on big projects would benefit from more.&lt;/p&gt;

&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;Gatsby Cloud at its core is built to improve the workflow of the web developer using Gatsby open source, by providing a tool to build at blazing speed and also cut build times by 90% to &amp;lt;10sec. Check out &lt;a href="https://willit.build/" rel="noopener noreferrer"&gt;willit.build&lt;/a&gt; which is a benchmark site that helps predict the build time of your site. &lt;/p&gt;

&lt;p&gt;I hope this helps and also I am happy to hear your opinions about Gatsby Cloud.  Would you use it? Anything I can help explain? &lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What do you think about GraphQL? Who's using it? Love it, hate it? Tell me why?</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Wed, 28 Oct 2020 20:39:32 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/what-do-you-think-about-graphql-who-s-using-it-love-it-hate-it-tell-me-why-5e5g</link>
      <guid>https://dev.to/obinnaspeaks/what-do-you-think-about-graphql-who-s-using-it-love-it-hate-it-tell-me-why-5e5g</guid>
      <description>&lt;p&gt;My story with learning and using GraphQL is a bittersweet one, I went from "What does this button do?" &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F297lf95ndn3a6fa8mc4g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F297lf95ndn3a6fa8mc4g.jpg" alt="Alt Text" width="754" height="558"&gt;&lt;/a&gt; to "WOOT WOOT"&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1sbhjerct47rk9c1mq3w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1sbhjerct47rk9c1mq3w.jpg" alt="Alt Text" width="800" height="626"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Why did you start using GraphQL? If you don't why not? &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>graphql</category>
    </item>
    <item>
      <title>This Filesystem API made my life easier.</title>
      <dc:creator>Obinna Ekwuno</dc:creator>
      <pubDate>Sat, 10 Oct 2020 20:48:54 +0000</pubDate>
      <link>https://dev.to/obinnaspeaks/this-filesystem-api-made-my-life-easier-3357</link>
      <guid>https://dev.to/obinnaspeaks/this-filesystem-api-made-my-life-easier-3357</guid>
      <description>&lt;p&gt;Building with static site generators is aimed at cutting off all the "difficult" parts and getting you started with serving content while using the same best practices and methods available. &lt;/p&gt;

&lt;p&gt;We have a lot of them (Gatsby, Hugo, Jykll), some more straightforward than others, each solving a particular use case, and to be frank, I would rather not compare them based on usage but on the Use case. At the end of the day, it all boils down to what you are trying to do.&lt;/p&gt;

&lt;p&gt;You see, the thing about abstraction is we have to know where to stop, as sometimes the solution might create a new problem. A problem that you have to solve. &lt;/p&gt;

&lt;p&gt;Here's my experience with a particular problem.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Programmatically creating pages in Gatsby.
&lt;/h2&gt;

&lt;p&gt;Now I love Gatsby, it makes a lot of things easier and in the process, it did open the door to a lot of other exciting things for me like GraphQL, and an API file called &lt;a href="https://www.gatsbyjs.com/docs/api-files-gatsby-node"&gt;Gatsby-node&lt;/a&gt;, where a lot of my early pains came from. &lt;/p&gt;

&lt;p&gt;The way Gatsby handles &lt;a href="https://www.gatsbyjs.com/tutorial/part-seven/"&gt;programmatically creating pages&lt;/a&gt;(which is a fancy way of saying, creating pages on demand by sourcing from a Content Management System) is in the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Source page via &lt;code&gt;Slug&lt;/code&gt; in Gatsby-node.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;createPages API&lt;/code&gt; to create a page for each slug.&lt;/li&gt;
&lt;li&gt;Resolve that in a template component path.&lt;/li&gt;
&lt;li&gt;Create that Path and then source for all the other information using the &lt;code&gt;slug&lt;/code&gt; as a unique identifier. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looks something like this, &lt;/p&gt;

&lt;h3&gt;
  
  
  Sourcing using &lt;code&gt;createPages API&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnvkkyww1ookfcw28usvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnvkkyww1ookfcw28usvj.png" alt="Alt Text" width="800" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolving in the Template directory
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu2uqz5bbat6dktw2qcfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu2uqz5bbat6dktw2qcfp.png" alt="Alt Text" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sounds like a lot yes? Hence, the cover image of this blog post. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0t1agnpvdb2gdrw0bq47.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0t1agnpvdb2gdrw0bq47.jpg" alt="What is going on" width="600" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of reasons &lt;a href="https://www.gatsbyjs.com/docs/why-gatsby-uses-graphql/"&gt;why Gatsby uses GraphQL&lt;/a&gt;, the most common is to facilitate sourcing data from different places and getting the exact thing you need. &lt;/p&gt;

&lt;p&gt;While this is a logical reason, this process isn’t as intuitive and sometimes might not be as straightforward as my steps make it seem.&lt;/p&gt;

&lt;p&gt;What if there was a way to make this more interesting, with fewer lines of code? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3mgyz2pqxg8lbvx59osw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3mgyz2pqxg8lbvx59osw.jpg" alt="Alt Text" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gatsby file system routes API
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;There is always a soul of good in things "evil" if people distill it out - Shakespeare &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was an answer to my WTF questions, an API built on top of the &lt;code&gt;createPages API&lt;/code&gt; to replaces the above steps with a more intuitive approach. This is how it works: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;use curly braces ({ }) in naming the file to signify dynamic URL segments that relate to a field within the node.&lt;/li&gt;
&lt;li&gt;write your query as you would for a particular page. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can leverage this feature by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GATSBY_EXPERIMENTAL_ROUTING_APIS=1 gatsby develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the END of this blog post.... (Joking) &lt;/p&gt;

&lt;p&gt;Ok seriously, Here's an example, If you would query a file like so :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="err"&gt;allProduct&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="n"&gt;nodes&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="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Gatsby always queries for id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&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 the file name in your pages directory would be &lt;code&gt;src/pages/products/{Product.name}.js&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Notice the pattern? Gatsby turns anything put into the pages folder to a route, so doing this automatically makes &lt;code&gt;/products/{Product.name}.js&lt;/code&gt; to a route. &lt;/p&gt;

&lt;p&gt;Here's what it turns the code block into: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ls0yxwqgqkddb2s5kl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ls0yxwqgqkddb2s5kl4.png" alt="Alt Text" width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the file name is in curly braces, that's about it. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This File system Routes API is built on top of the &lt;code&gt;createPages API&lt;/code&gt; so you will not have to refactor your existing applications 😉 &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition to the simplification, Gatsby automatically includes a gatsbyPath field on every model used by collection pages. &lt;/p&gt;

&lt;p&gt;The gatsbyPath field must take an argument of the filePath it is trying to resolve. This is necessary because it’s possible that one model is used in multiple collection pages. &lt;/p&gt;

&lt;p&gt;This is an example of what the gatsbyPath looks like in a query:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuvc94qhz6zfphwd28rok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuvc94qhz6zfphwd28rok.png" alt="Alt Text" width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feature is still experimental and as it gets built out I am sure it will create a more interesting way to query pages in Gatsby. &lt;/p&gt;

&lt;h2&gt;
  
  
  Wait there is more...
&lt;/h2&gt;

&lt;p&gt;This file system API also helps with creating client-only route. Previously, to access dynamic content that isn’t known to Gatsby at build time,(Which is referred to as client-only routes) you would have to create a route with one or more dynamic segments to query data from a server in order to render your page.&lt;/p&gt;

&lt;p&gt;For example, to show a user a personalized settings page, in order to edit a user, you might want a route like &lt;code&gt;/user/:id&lt;/code&gt; to fetch the data for whatever id is passed into the URL. You can now use square brackets ([ ]) in the file path to mark any dynamic segments of the URL.&lt;/p&gt;

&lt;p&gt;This means &lt;code&gt;src/pages/users/[id].js&lt;/code&gt; would look like this  &lt;code&gt;/users/:id&lt;/code&gt;. Gatsby also supports splat routes, learn more about this in the &lt;a href="https://www.gatsbyjs.com/docs/file-system-page-creation/#routing-and-linking"&gt;documentation&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;There you have it, folks. I believe that with a few improvements to other pain points of Gatsby, some of the processes will be less stressful. &lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>graphql</category>
    </item>
  </channel>
</rss>
