<?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: Bryan Robinson</title>
    <description>The latest articles on DEV Community by Bryan Robinson (@brob).</description>
    <link>https://dev.to/brob</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F173278%2F96678344-7d51-41ca-8ff4-78343d865e05.jpg</url>
      <title>DEV Community: Bryan Robinson</title>
      <link>https://dev.to/brob</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brob"/>
    <language>en</language>
    <item>
      <title>Does our technology still work for us?</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Tue, 04 Feb 2025 14:35:48 +0000</pubDate>
      <link>https://dev.to/brob/does-our-technology-still-work-for-us-55e2</link>
      <guid>https://dev.to/brob/does-our-technology-still-work-for-us-55e2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Google’s search algorithm is geared to find the best content and UX for their users. Write the best content that helps people accomplish their tasks, and you’ll succeed in an SEO race.” - Me, circa 2012&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was solid advice during that era. I gave it to many marketing and website clients.&lt;/p&gt;

&lt;p&gt;Write for humans and the algorithm would reward you. Make your content useful, and the algorithm would reward you. Have the best UX, and the algorithm would reward you.&lt;/p&gt;

&lt;p&gt;The tool worked for us.&lt;/p&gt;

&lt;p&gt;It was a symbiotic relationship. Google’s nascent ad functionality benefited from being THE place to find information. Users benefited by quickly finding what they needed. Websites and content creators benefited with larger audiences, focused and happy users, and a clear-cut path toward success. We all got what we needed out of the relationship.&lt;/p&gt;

&lt;p&gt;In other words: the incentives matched for everyone.&lt;/p&gt;

&lt;p&gt;AI Leaders want humans to work for the tool&lt;br&gt;
The symbiosis was always a delicate balance. If you study nature, you’ll know that any symbiotic network is always a delicate balance. Drift too far in one direction or another and risk toppling the entire structure.&lt;/p&gt;

&lt;p&gt;Tech leaders no longer seem interested in the balance. They seem to want humans to work for the tools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It would be impossible to train today’s leading AI models without using copyrighted materials…” - &lt;a href="https://www.salon.com/2024/01/09/impossible-openai-admits-chatgpt-cant-exist-without-pinching-copyrighted-work/" rel="noopener noreferrer"&gt;Sam Altman&lt;/a&gt; CEO OpenAI.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;blockquote&gt;
&lt;p&gt;“Platforms, tools or frameworks that are hard for large language models (LLMs) and agents to use will start feeling less powerful and require more manual intervention. In contrast, tools that are simple for agents to integrate with and well suited for the strengths and constraints of LLMs will quickly become vastly more capable, efficient and popular.” &lt;a href="https://biilmann.blog/articles/introducing-ax/" rel="noopener noreferrer"&gt;Mat Biilman&lt;/a&gt;, CEO Netlify&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s break down a few propositions from these quotes.&lt;/p&gt;

&lt;p&gt;Altman’s meaning is relatively simple: “The tool I’m building won’t work without your content. All of your content.”&lt;/p&gt;

&lt;p&gt;Biilman’s quote is a bit more nuanced, but no better. In its essence, it says the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Agents and LLMs need to be able to use your site or tool&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sites and tools should be made easier for bots to use to be “more powerful” to end users&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the tools are more powerful, they will be more popular&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those propositions are stated directly. But what logically follows from those propositions? Two quiet corollaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1st Corollary: If the AI tools are more powerful, users won't use your tool; their bot will. Users won’t read your content; their bot will summarize it&lt;/li&gt;
&lt;li&gt;2nd Corollary: If users only use their AI tool, and it doesn’t send them to your site, app, or product, the AI tool user won’t be your audience or your user. That person will be captured in the AI company’s ecosystem (💰, 👀, 🕦)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, the message is clear: Give AI your content. Give AI access to your tools. And, in the end, let the agent and AI company take credit for your work.&lt;/p&gt;

&lt;p&gt;The incentives don’t match.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO worked for us… until it didn’t
&lt;/h2&gt;

&lt;p&gt;I worked in newspapers for the first six years after I graduated college (2006-2012). To put this time period in context, Google’s search algorithm still ranked partially based on the keywords meta tag for half that time (what a time to be alive… a time when your article on technology would have keywords about Britney Spears just to get noticed…)&lt;/p&gt;

&lt;p&gt;I grew very familiar with the nature of SEO while working on the web for an organization which made money off the discoverability of its content. We were being given millions of views across our news outlets each month via search (specifically Google search). Do you know what the takeaway from our executives (and other executives in the industry) was?&lt;/p&gt;

&lt;p&gt;“Google is stealing our content!”&lt;/p&gt;

&lt;p&gt;Young and idealistic about the internet, I couldn’t handle that. It was, honestly, ridiculous. We were literally generating revenue from the views we got. Google was bringing us an audience for each story we published.&lt;/p&gt;

&lt;p&gt;My, how the times have changed me…&lt;/p&gt;

&lt;p&gt;In today’s ecosystem, I’d be forced to agree with those executives. Forty-year old Bryan is mature enough to admit that, but it’s still painful.&lt;/p&gt;

&lt;p&gt;Google at the time wanted to make information discoverable (or at least searchable… if you’ve watched my &lt;a href="https://www.youtube.com/watch?v=S0yCakxuFMU" rel="noopener noreferrer"&gt;search vs. discovery video&lt;/a&gt;, you may know about the distinction). As time marched on, and Google ads became more important (and thus user engagement became more important), Google wanted fewer users leaving the search page.&lt;/p&gt;

&lt;p&gt;It started as a boon for Google’s search users. Search for a local restaurant, get a widget in the sidebar (nice and out of the way) with important information like the phone number, address, and even a link to the website. Nice!&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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F1c257d79598e377b51ae0c4dbeab5cdb8edb43cf-1558x1604.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" 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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F1c257d79598e377b51ae0c4dbeab5cdb8edb43cf-1558x1604.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" alt="&amp;quot;Places&amp;quot; screenshot on Google showing 3 locations for Central BBQ in Memphis along with their phone number and whether or not they're open" title="&amp;quot;Places&amp;quot; screenshot on Google showing 3 locations for Central BBQ in Memphis along with their phone number and whether or not they're open" width="800" height="823"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But then we got featured snippets… Now, when you search “What is an API?” you get an answer (pulled and quoted from a site) right in the results… with no real benefit to the original author.&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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F345c3921cf3e599889b51bdd054b000c75ed1c4c-2954x1020.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" 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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F345c3921cf3e599889b51bdd054b000c75ed1c4c-2954x1020.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" alt='Google result for "What is an api?" Showcasing a snippet from an AWS blog post explaining what an API is in enough detail where a user may not click the original link' title='Google result for "What is an api?" Showcasing a snippet from an AWS blog post explaining what an API is in enough detail where a user may not click the original link' width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, we got Google shopping results. I started writing this on a fresh legal pad acquired from a local Walgreens this morning. Walgreens was on my way to the library where I decided to write. I did a quick Google search to check stock. I wanted to quickly find the dedicated page on Walgreen’s site. Instead, I found this:&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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F363f5b7acbe0d8a3854390bcdc053d4eda5823a8-3262x1806.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" 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%2Fcdn.sanity.io%2Fimages%2Fmyf3wh95%2Fproduction%2F363f5b7acbe0d8a3854390bcdc053d4eda5823a8-3262x1806.png%3Fw%3D960%26fit%3Dmax%26auto%3Dformat" alt='Screenshot of a "shopping" result from Google search of "Walgreens legal pad" showcasing 8 products in a grid (2 from walgreens, the rest from other vendors) and one regular result below. The grid is all "sponsored"' title='Screenshot of a "shopping" result from Google search of "Walgreens legal pad" showcasing 8 products in a grid (2 from walgreens, the rest from other vendors) and one regular result below. The grid is all "sponsored"' width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, the page I was looking for is listed three times, but it’s listed in a confusing mess of six other products. Those other products are from their competitors! All of these posts (including the Walgreens grid results) are sponsored! It certainly doesn’t seem to be working for Walgreens. It seems to mostly be working for the company selling the ads…&lt;/p&gt;

&lt;p&gt;It’s harder to get real views. It’s harder to transfer the engagement to the individual. There’s higher competition around products (even in branded search). And all of this before the advent of “AI Search Summaries,” which when launched had no attribution on answers and even now are unclear on a path to getting users to the original sources.&lt;/p&gt;

&lt;p&gt;The algorithm once worked for us (both the creators and the users). Then, it became more important for Google to hold the attention to earn money. This made the incentive structure flip.&lt;/p&gt;

&lt;p&gt;Our products, our content, our news exists to help Google’s product, not ours. Perhaps those news executives were right…&lt;/p&gt;

&lt;p&gt;There’s no way around it: we have to talk about capitalism&lt;br&gt;
In essence, the degradation of this symbiotic relationship can be laid at the feet of capitalism. The balance of power had shifted in the early 2000s, as small companies were able to make money on the web. Largely helped by early Google, but there’s always a tipping point. And that brings us to another issue with the quotes from earlier: in this game, who benefits the most?&lt;/p&gt;

&lt;p&gt;The last section on search issues covers one of our capitalistic problem: when multi-billion dollar companies see a way to make more money, it’s hard for them to hold their original values. If we help agents and invest in making things easier for them, who are we helping? The agents are not benefitting. They’re not alive, sentient, or actually intelligent. So who? The multi-billion dollar companies and the venture capital (VC) firms behind them. They get user lock-in and, as we’ve seen with the devolution of Google search, that social contract can be renegotiated.&lt;/p&gt;

&lt;p&gt;Let’s move, instead, back to Altman’s quote. We’ve got to give up all our copyrighted material to achieve a great and glorious GenAI world. A world where all the powers of creation are at the disposal of everyone!&lt;/p&gt;

&lt;p&gt;Look. Creative work is hard. I get it. I’ve always wanted to be a cartoonist (and an actor… and a novelist… and a film maker), but I’ve never put in the work to get good (or even the effort to get mediocre). I get the allure of being able to type a prompt and get an artifact back: Comic art, landscape painting, cool background music, amazing video of an imaginary cityscape.&lt;/p&gt;

&lt;p&gt;But here’s the thing about capitalism and creativity: businesses don’t want to pay for creative output. Ask anyone who has tried to make a living as a commercial artist. Pay is terrible, hours are long, and the work is constantly devalued.&lt;/p&gt;

&lt;p&gt;Imagine the joy, then, when investors learned they could do away with those tiny payments to those pesky artists. That’s where the VC fervor and hype comes from. Not giving these tools to artists, but instead giving them to low-paid prompt engineers to lower the cost of creative output.&lt;/p&gt;

&lt;p&gt;Oh, and let’s not even mention the software development side of the “Creatives” industry. We’ve been making too much money for too long. Wouldn’t it be better if a founder, investor, or product manager could just make “the next big thing” without a team of expensive engineers? $10 million dollars in seed funding right away!&lt;/p&gt;

&lt;p&gt;But honestly, that’s not the worst of it. The worst is that creation is a core trait of humanity. We seek to create. As we learn and level up our creative skills, we often find bigger and better applications for them.&lt;/p&gt;

&lt;p&gt;The AI scheme is to take the creative tasks away from humans so that we can be “more productive.” And while that may be an economic boon in the short term, it’s a real problem long term as the creative skills of humanity are slowly removed.&lt;/p&gt;

&lt;p&gt;“That’s hyperbole!” I hear some of you cry (or at least, I hear the AI advocates cry).&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;Though, maybe not…&lt;/p&gt;

&lt;h2&gt;
  
  
  AI’s hidden cost on our brains
&lt;/h2&gt;

&lt;p&gt;It’s perhaps too early in the current technology cycle for there to be definitive studies on AI’s affect on humanity. Even still, we can look at something very similar.&lt;/p&gt;

&lt;p&gt;There have been many studies on “The google effect.”&lt;/p&gt;

&lt;p&gt;At its essence, the google effect says that by offloading our data retention and recollection to the internet, our brains are worse at retaining and accessing information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Moreover, when people search for information on the Internet while working on the Internet, they are more likely to use the Internet rather than their brain the next time they encounter that issue, and they retain pertinent information in an interesting way: they remember the Internet address where the pertinent information is stored (e.g., domain name, database, etc.). People who have searched the Internet for a solution to a problem, for example, will remember the website where they found the solution more vividly when they encounter the problem again (50), even if they have forgotten the precise essence of the problem for which they were searching.“ - &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC10830778/" rel="noopener noreferrer"&gt;NIH Research paper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“People are more likely to return to the Internet and repeat the search process when confronted with a similar issue if they remember the location where the information was saved. Moreover, people’s perceptions of findability roughly predict the amount of time it will take them to find the information they need on the Internet, which increases their reliance on it laterally (38). Moreover, when searching for solutions on the Internet, rapid responses (answers that are obtained more quickly) are more likely to be convincing. People feel more confident when working in an Internet-accessible environment, and “Google effect” is stronger for those who have previously used the Internet.” - &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC10830778/" rel="noopener noreferrer"&gt;NIH Research paper&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s certainly not great. We’ve modified our neural pathways, and not for the better.&lt;/p&gt;

&lt;p&gt;While not yet studied, it stands to reason that if we outsource our artistic, writing, music, and even coding skills, the parts of our brains that help in creation would slowly function worse and become dependent on those tools (in the same way they have with Google search).&lt;/p&gt;

&lt;p&gt;Definitely great for the person who sells what we become dependent upon. Definitely bad for core elements of our human nature.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“In addition, by examining the potential costs and limitations of the Internet, individuals are in a better position to develop and modify the technology so that it is potentially more productive, less disruptive, and more consistent with the everyday goals and functions of human cognition.” - &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC10830778/" rel="noopener noreferrer"&gt;NIH Research paper&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The study also says that the more people understand this, the more informed they are, the better the decisions they can make for themselves. Since learning this, I even attempt minutes of thought to remember information rather than do an immediate search. Better to start rebuilding those synapses, after all.&lt;/p&gt;

&lt;p&gt;But those sorts of solutions assume people are able to come to that understanding. The companies working on this have billions invested… Kind of puts the use of “dependent” in perspective…&lt;/p&gt;

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

&lt;p&gt;My point in all of this is that when people start telling you that you need to work for your tools instead of the other way around, you need to DEEPLY inspect that.&lt;/p&gt;

&lt;p&gt;We have very recent examples of how this has worked. It always starts at “Democratizing” powerful things (creation, development, writing, videos) and, so far, ends with the most benefits in the hands of large companies. When that happens, we lose the democratization, but with AI, we run the very real risk of losing the skills that make culture, the skills the benefit us all, the skills at creation.&lt;/p&gt;

&lt;p&gt;I hope we don’t let them take that from us, just to make us “more productive.”&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>ux</category>
    </item>
    <item>
      <title>Pay no mind to this, this is just a test</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Fri, 25 Oct 2024 18:02:34 +0000</pubDate>
      <link>https://dev.to/brob/pay-no-mind-to-this-this-is-just-a-test-3f4h</link>
      <guid>https://dev.to/brob/pay-no-mind-to-this-this-is-just-a-test-3f4h</guid>
      <description>&lt;p&gt;Just a test, y'all. Nothing to see here.&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>Convert a Hygraph Next.js project to Next.js app directory</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Mon, 11 Sep 2023 04:00:00 +0000</pubDate>
      <link>https://dev.to/hygraph/convert-a-hygraph-nextjs-project-to-nextjs-app-directory-beta-4m13</link>
      <guid>https://dev.to/hygraph/convert-a-hygraph-nextjs-project-to-nextjs-app-directory-beta-4m13</guid>
      <description>&lt;p&gt;In late 2022, the Next.js team launched their next stable version: Next.js 13. While this update came with a lot of improved functionality, one new feature is set to redefine the way you build with Next: the app directory beta.&lt;/p&gt;

&lt;p&gt;The app beta is not used default. It needs to be enabled and can then be incrementally adopted. In this article, we're going to take a look at the new app structure and convert an existing Hygraph project to this new structure. We'll take this step by step and see how incremental adoption really works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required Knowledge
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A basic understanding of &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;a href="https://hygraph.com/learn/graphql" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;a href="https://hygraph.com" rel="noopener noreferrer"&gt;Hygraph&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the app structure?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.graphassets.com%2Fd7bqvqN5Rq6PgNQO955j" 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%2Fmedia.graphassets.com%2Fd7bqvqN5Rq6PgNQO955j" alt="The new file structure allows for much more flexibility including the adding of specific templates for each route such as not-found, loading, head, and more" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new app directory structure isn’t just a reorganization of your project (though it is, and that reorganization feels powerful). This new methodology utilizes &lt;a href="https://beta.nextjs.org/docs/rendering/server-and-client-components#why-server-components" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt; to bring many new features and mental models. With Server components, a developer can do their business logic in a React component that doesn’t need to be sent to the client. This allows for a lot less JavaScript to be sent to the browser. If you have frontend needs, you can then use client components that get sent individually to the browser. This should reduce your bundle size down to a more manageable state. Just the code you need for interactivity.&lt;/p&gt;

&lt;p&gt;The overall data fetching model has shifted, as well. Next has extended the &lt;a href="https://beta.nextjs.org/docs/api-reference/fetch" rel="noopener noreferrer"&gt;native fetch() method&lt;/a&gt; from Node and the browser APIs and has added additional caching and deduplication functionality. This means you can create requests for data in various files, but the request will only fire to your API once. Fetching your blog posts to create a list on your homepage and to create the blog page list? Now that’s 1 API request, despite writing the code in multiple places. This gives a lot of developer convenience while creating better build and serve times.&lt;/p&gt;

&lt;p&gt;There are even more new and improved features, but to use them, we need to be able to upgrade our project to use this new structure. It’s not as simple as moving your page files into the app directory, so let’s dig in and get things updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a Next.js 13 project to work with the app directory
&lt;/h2&gt;

&lt;p&gt;To start, we need a Next.js project. While this process is possible with any Next project using data fetching, let’s start from our simple Next example from the &lt;a href="https://github.com/hygraph/hygraph-examples/tree/master/with-nextjs" rel="noopener noreferrer"&gt;hygraph-examples GitHub repository&lt;/a&gt;. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx degit hygraph/hygraph-examples/with-nextjs with-nextjs
&lt;span class="nb"&gt;cd &lt;/span&gt;with-nextjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we install, we’ll need to update some dependencies in the package.json file. Let’s upgrade Next to &lt;code&gt;^13.0.0&lt;/code&gt; and remove the &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-doc&lt;/code&gt; dependencies.&lt;/p&gt;

&lt;p&gt;Your package.json should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hygraph-with-nextjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next start"&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;"dependencies"&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;"graphql-request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.8.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.0.0"&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;Now we can start the development server by running &lt;code&gt;npm run dev&lt;/code&gt;. Immediately on loading the local version, we’re presented with the first update. Since this was previously not running Next 13, the Link component is structured wrong. In Next 13, we no longer need to have an anchor tag inside the Link component. This is a great update, but we need to change these references. The only location the Link component is used is the homepage in &lt;code&gt;/src/pages/index.js&lt;/code&gt;. Open that file, and remove the &lt;code&gt;&amp;lt;a&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt; tags to get the site to run&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;// Old&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&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="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;`/products/&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="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="o"&gt;&amp;gt;&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="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;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// New&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&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="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;`/products/&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="o"&gt;&amp;gt;&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the site runs, let’s update from the Pages directory to the new app directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting from /pages to /app
&lt;/h2&gt;

&lt;p&gt;Before we can convert to the app directory, we need to let Next know that we’ll be using this experimental feature. To do that, we need a configuration file. In the root of our project, let’s create a &lt;code&gt;next.config.js&lt;/code&gt; file. Inside that file, add the following basic configuration.&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="cm"&gt;/** @type {import('next').NextConfig} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;appDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, we can create our app directory. Inside the src directory, add an &lt;code&gt;app&lt;/code&gt; directory. We’ll be moving each of our routes to this directory. The nice thing is that each of our older routes will continue to work as we make these changes, since both directories can work at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the root layout component
&lt;/h3&gt;

&lt;p&gt;One of the first big changes to the overall structure is the need of a “root layout.” This will be the scaffolding of Markup for all pages. This can start very simply, but have any global information that each route will need. The &lt;code&gt;RootLayout&lt;/code&gt; function accepts a children object. That children object will contain the Server Components that we’ll add via each page we create.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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;html&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&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;body&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;children&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;/body&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;/html&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this layout, we can now add our first page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the homepage
&lt;/h3&gt;

&lt;p&gt;Instead of the &lt;code&gt;index.js&lt;/code&gt; convention of the &lt;code&gt;/pages&lt;/code&gt; directory, each page in the app directory will be named &lt;code&gt;page.js&lt;/code&gt;. In the root of the app directory, create a new &lt;code&gt;page.js&lt;/code&gt; file. This will be our homepage.&lt;/p&gt;

&lt;p&gt;Once you create this page, Next will thrown an error in the console. There is now a conflicting route: &lt;code&gt;/src/pages/index.js&lt;/code&gt; is the same route as &lt;code&gt;/src/app/page.jsx&lt;/code&gt;. For now, rename &lt;code&gt;/src/pages/index.js&lt;/code&gt; to &lt;code&gt;oldIndex.js&lt;/code&gt;. We’ll delete this file later, but this file is the blueprint for our new file.&lt;/p&gt;

&lt;p&gt;Once we rename the file, we get a new error. The &lt;code&gt;page.jsx&lt;/code&gt; file is not exporting a React component. In fact, it’s exporting anything, since it’s blank. Let’s fix that and export a simple React component with an h1 of Products.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&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="o"&gt;&amp;lt;&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="nx"&gt;Products&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the homepage should have the H1 instead of the product list from the old homepage. Let’s get the list of products back.&lt;/p&gt;

&lt;p&gt;In the old index file, we export a function called &lt;code&gt;getStaticProps&lt;/code&gt; to get and pass the data to our page component. In the new structure, we don’t need to export anything or name things in any specific ways. Instead, we can create a regular async function to fetch our data.&lt;/p&gt;

&lt;p&gt;Before our Server Component in the new &lt;code&gt;page.jsx&lt;/code&gt; file, create a new async function named &lt;code&gt;getProducts&lt;/code&gt;. This run a fetch request to the Hygraph endpoint for the project and return the products array. We can then run that function in our Server Component to loop through the data and display a link to each page.&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="nx"&gt;Link&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;next/link&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;getProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api-eu-central-1.hygraph.com/v2/ck8sn5tnf01gc01z89dbc7s0o/master&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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;application/json&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;Accept&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{
                products {
                  slug
                  name
                  id
                }
              }`&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&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;products&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;getProducts&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;&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="nx"&gt;Products&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;ul&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;products&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;product&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&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;`/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;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;/ul&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;/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things have changed between our old version and new. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since we’re not using &lt;code&gt;getStaticProps&lt;/code&gt;, we no longer need to structure our return in a specific way. Instead, we just return back the array of products&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;fetch&lt;/code&gt; instead of &lt;code&gt;GraphQLRequest&lt;/code&gt;. By using fetch, we allow Next to cache the information and if we execute an identical fetch later, it won’t make a separate call, but instead use this data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you save this in, we have a functioning homepage. These links even take us to the pages associated with our old dynamic routes — since &lt;code&gt;/app&lt;/code&gt; and &lt;code&gt;/pages&lt;/code&gt; can work incrementally. That could be a fine stopping point, but let’s convert our dynamic route over, as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting the dynamic products route
&lt;/h3&gt;

&lt;p&gt;Just like the homepage, we’ll start by setting up our structure. Instead of the dynamic route brackets happening on the file, hey happen on the directory. So the &lt;code&gt;/pages/products[slug].js&lt;/code&gt; will change to be &lt;code&gt;/app/products/[slug]/page.jsx&lt;/code&gt;. This allows for more flexibility and the ability to add custom, co-located features such as error pages, loading pages, and custom layouts.&lt;/p&gt;

&lt;p&gt;Create the new file and add a simple component like we did for the homepage.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&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="o"&gt;&amp;lt;&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="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;product&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the application won’t immediately error, since the static pages aren’t being rebuilt, but if you restart Next, it will throw the same error as before about conflicting files. Delete or rename the file and we’ll move forward.&lt;/p&gt;

&lt;p&gt;The overall structure of this file is significantly different. Just like the homepage, we no longer need the getStaticProps function, but we also don’t need the getStaticPaths either. These are all files that render on the server, so we can move all that information into a single async function that can happen at request time.&lt;/p&gt;

&lt;p&gt;The new Server Component gives us a params object that we can use to get the slug for the current route. We can pass that into a new getProduct() function and use that as a variable in our query to Hygraph. This will fetch the data for that specific product. We can then use that in the Markup generated by our component to display the title, description, and price.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api-eu-central-1.hygraph.com/v2/ck8sn5tnf01gc01z89dbc7s0o/master&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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;application/json&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;Accept&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{
                product(where: {slug: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"}) {
                    name
                    description
                    price
                }
            }`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;variables&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;params&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="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&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;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&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;product&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="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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&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;p&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have all the pages working together. Let’s take this one step further and add a custom 404 page for any products that aren’t found.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up a custom 404 page
&lt;/h3&gt;

&lt;p&gt;Because of the new app structure, we can colocate all of our relative files. In this case, if we want a custom 404 page, we can add a &lt;code&gt;not-found.jsx&lt;/code&gt; template in the &lt;code&gt;/products/[slug]&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The file will export a Server (or client) component, and for now, we’ll have a very simple message. In this case, since we’re looking for a product, let’s be specific and say “Product not found” instead of a simple “Page not found.”&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NotFound&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="o"&gt;&amp;lt;&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="mi"&gt;404&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;Product&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;found&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;/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, navigate to a route that doesn’t match any slug in Hygraph: try &lt;a href="http://localhost:3000/products/sdlfkjl/" rel="noopener noreferrer"&gt;http://localhost:3000/products/sdlfkjl/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of getting a 404 page, we receive an unhandled error. That’s because we need to tell Next when we know it’s not found. In this case, if our query to Hygraph returns no results, we need to throw a 404.&lt;/p&gt;

&lt;p&gt;After calling the getProduct() function, we can check if product is defined and if not, run the notFound() function from &lt;code&gt;next/navigation&lt;/code&gt;. This will do a few things for us. It will redirect the browser to the NotFound component and automatically add a "noindex" meta tag to these routes. This is good SEO practice and will keep your site’s 404 pages from being indexed by search engines.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&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;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;notFound&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;&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;product&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="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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&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;p&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have a fully-functioning 404 page! &lt;/p&gt;

&lt;p&gt;From here, you can delete the /pages directory and move to working on your app. You’ll still use the pages directory for your API routes, as well as any static routes you want to use, but most things can be used to this new, more-powerful version of Next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;With that, we have fully converted our simple Hygraph Next.js example from /pages to /app. At this point, I’d suggest taking look at all the different file types you can create in the colocated directories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a head.jsx file to specify meta data such as title, description, and more for each product page&lt;/li&gt;
&lt;li&gt;Add a loading.jsx file for loading page for longer queries.&lt;/li&gt;
&lt;li&gt;Add an error.jsx file to handle other error types.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Boost your editorial experience with distributed content management</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Thu, 07 Sep 2023 18:09:56 +0000</pubDate>
      <link>https://dev.to/hygraph/boost-your-editorial-experience-with-distributed-content-management-50ni</link>
      <guid>https://dev.to/hygraph/boost-your-editorial-experience-with-distributed-content-management-50ni</guid>
      <description>&lt;p&gt;Tools are only good if users want to use them. If your writers and editors don’t want to use a tool, your content velocity will halt, and your business will suffer.&lt;/p&gt;

&lt;p&gt;While it’s easy to dictate a monolithic approach to building your CMS strategy, managing a diverse team with diverse needs in their content generation processes is not always easy. Rarely will one data entry process be the correct one for all those individuals.&lt;/p&gt;

&lt;p&gt;It's important to note that while allowing your writers and editors to choose their tools can increase content velocity, it's also important to maintain some standardization of data structures and APIs. Without that standardization, your developer velocity will suffer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is distributed content management?
&lt;/h2&gt;

&lt;p&gt;Distributed content management is a pattern where your content is spread amongst the systems that work with that content type the best. Your marketing copy lives in a &lt;a href="https://hygraph.com/learn/headless-cms" rel="noopener noreferrer"&gt;headless CMS&lt;/a&gt;; your product information in your PIM; your inventory in your WMS; your customers in your CRM. Each system handles its own data type better than a monolithic approach could.&lt;/p&gt;

&lt;p&gt;Each data type also has its own specific group of editors. These editors are most comfortable in the systems they’re trained to deal with. Forcing an eCommerce expert into a headless CMS or a marketing expert into a PIM just for the sake of having a singular tool doesn’t make sense and will often stifle the teams needed to continue refining and producing content.&lt;/p&gt;

&lt;p&gt;The distributed CMS pattern means each editor type can use the system that works best for their data. The data is then all brought together via a federation pattern — here at Hygraph, we’re believers in &lt;a href="https://hygraph.com/blog/introducing-content-federation" rel="noopener noreferrer"&gt;Content Federation&lt;/a&gt;, but any data combination pattern will work depending on your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you care about content velocity?
&lt;/h2&gt;

&lt;p&gt;Among many teams, content reigns supreme. Strong content published regularly is crucial for long-tail leads, thought leadership, and brand growth.&lt;/p&gt;

&lt;p&gt;Regularly creating fresh, new content can help keep your audience engaged and growing. While a single strong piece of content can provide high value, each new content piece is a chance to find a bigger, more engaged audience.&lt;/p&gt;

&lt;p&gt;Creating a steady stream of new content pieces also allows for a stronger sense of experimentation. In the realm of content, what works one week may not work the next. Trying new perspectives, content types, and tones is important. Experimentation increases the chance of hitting the right tone and perspective to generate high engagement. Not every piece will succeed, which is why it’s so important to be able to create and experiment at a high velocity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The importance of content standardization
&lt;/h2&gt;

&lt;p&gt;Content velocity might increase, but there are potential pitfalls to a distributed CMS approach that centers on the idea of standardization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified Content Strategy
&lt;/h3&gt;

&lt;p&gt;When you lose a centralized tool, it’s easy to let strategic concerns slip in favor of “getting the work done.” Any content plan needs a solid strategy as its base. While that can be covered up by a strong centralized tool, holes in internal workflows and strategies become more apparent when decentralized.&lt;/p&gt;

&lt;p&gt;When crafting a decentralized solution, it’s important to start with a set of standards for how all aspects will be crafted. This goes from things like tags on blog posts and articles to how products are referenced. A strong plan for this content will align everyone in your organization around how these elements should be written, edited, and used regardless of the system they use. A strong plan will also help create a stronger sense of technological standardization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technological standardization
&lt;/h3&gt;

&lt;p&gt;While allowing your writers and editors to choose their tools is important, it's equally important to maintain some standardization in your data structures and APIs. This will ensure that your developer velocity doesn't suffer and that your content can be easily integrated into your CMS.&lt;/p&gt;

&lt;p&gt;Given three content teams with three separate CMSs, chances are good that your development team will struggle with creating consistent, efficient, and performant applications and sites. Each CMS brings its own data structures, its own API endpoints, and its own content strategy. Those can’t be standardized in their individual applications; a central system must standardize them. Without a standardization mechanism, site and application development will be more prone to bugs, developer frustrations, and blockers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How a distributed system can help with standards
&lt;/h3&gt;

&lt;p&gt;Each system may be separate, but each can help enforce the standards of the others.&lt;/p&gt;

&lt;p&gt;For example, Dr. Oetker, a global food and beverage wholesaler, has multiple systems that need to converge to make their various localized websites, customer data needs, product information, and recipe database.&lt;/p&gt;

&lt;p&gt;While all of these systems could theoretically exist within one massive, monolithic structure, each system is better at maintaining the individual standards of its own content than could happen in a singular system.&lt;/p&gt;

&lt;p&gt;Dr. Oetker’s product information lives within their PIM. The PIM provides product data for the recipe database and editorial CMS. Customer data is housed in Force.com. Page data and content structure are built in Hygraph, where editors also work. All this is bundled and built with Next.js for 40 localized websites.&lt;/p&gt;

&lt;p&gt;A recipe editor won’t be editing the product information. Since that lives in the PIM, it’s completely safe and standardized. Same for building editorial content or pages. All the product information is usable and standardized without having to worry about the individual teams knowing exactly the right information.&lt;/p&gt;

&lt;p&gt;We leave room for error or interpretation if we depend on an overall strategy and the humans implementing them in separate systems. However, if the information is pulled directly from the PIM or WMS systems for use in the others, then the standards set by the PIM or WMS team are automatically enforced for all other teams.&lt;/p&gt;

&lt;p&gt;
  Recommended Reading
  &lt;p&gt;Case study: &lt;a href="https://hygraph.com/case-studies/dr-oetker" rel="noopener noreferrer"&gt;Dr. Oetker harmonizes platform infrastructure with Hygraph&lt;/a&gt;&lt;/p&gt;



&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced training time and costs
&lt;/h3&gt;

&lt;p&gt;When you dictate the tools that your writers and editors use, you must provide training and support for those tools. This can be time-consuming and expensive, especially if you have a large team. Instead, if you work with your teams and allow them to collaborate and help craft the editing experience, including the tools, you can gain buy-in and drastically reduce training time and costs. When drafting a content architecture and strategy, &lt;a href="https://hygraph.com/blog/essential-guide-to-content-modeling" rel="noopener noreferrer"&gt;involving your editors as early as possible&lt;/a&gt; can help save on inconsistencies later.&lt;/p&gt;

&lt;p&gt;Since your team is already familiar with those tools, they won't require as much training, and you won't need to spend as much on support. This can free up more time and resources for other critical tasks, such as content strategy and distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a distributed CMS
&lt;/h2&gt;

&lt;p&gt;While a multi-CMS approach may feel like it adds a lot of overhead or orchestration, it’s possible to set this up in a real-world approach with relatively little effort.&lt;/p&gt;

&lt;p&gt;In the past, this would have required custom orchestration layers. Whether those were hosted middleware functions, content hub approaches, or an incredibly bloated frontend, it often felt insurmountable. We can easily overcome these deficiencies with recent improvements in the federation world. While tools like Apollo made the development of orchestration layers easier, concepts like Content Federation allow for ease of use for developers and non-technical users to create orchestration layers.&lt;/p&gt;

&lt;p&gt;With Content Federation, we can craft a system where multiple data sources come together in a low-code or no-code system and allow for data to be remixed in various ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  An eCommerce experience
&lt;/h3&gt;

&lt;p&gt;Let's look at an eCommerce store as an example of real-world distributed content management. To create an eCommerce experience, we need product information, blog posts, page content, user reviews, and more. No single CMS provides the ideal solution for all these types of data.&lt;/p&gt;

&lt;p&gt;Product information is often best housed in your product information management (PIM) solution — Shopify, BigCommerce, Magento, etc. These PIMs are great for eCommerce data but are notoriously bad for marketing content and are often not fully featured enough for complex inventory management, where a warehouse management solution (WMS) would be a better tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkn9jcla5ickot3g2zka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkn9jcla5ickot3g2zka.png" alt="Graphic showing 3 data sources flowing into a headless CMS whic then communicates to the frontend" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When crafting marketing pages, you’ll want a CMS with a solid foundation of both data and content and the ability to combine those in some form of page-building system. We may be biased, but Hygraph is a great solution here.&lt;/p&gt;

&lt;p&gt;Finally, your content team may already have a blog in a system like WordPress. Say what you will about WordPress, but it’s still a solid blogging experience. Its page-building experience is weak, as is its PIM experience.&lt;/p&gt;

&lt;p&gt;Each of these platforms offers specific perks and deficiencies. Combining all of them together can create a great experience for all editors involved. Let’s make sure it’s also a great experience for developers by combining all of the necessary data into one API.&lt;/p&gt;

&lt;p&gt;Using Hygraph’s Remote Sources functionality, we can pull all the data from our PIM for product details, inventory levels from our WMS, and all the blog posts from WordPress and combine them with marketing data and campaigns stored in Hygraph to create a full API of all the data needed to build experiences across multiple channels.&lt;/p&gt;

&lt;p&gt;On top of that, any data can be augmented and constructed in new ways. Products can be added to campaign pages. Specific reviews and blog posts can be built into the homepage (and changed by editors). All of this is unlocked when you use a CMS as your middleware.&lt;/p&gt;

&lt;p&gt;
  Recommended Reading
  &lt;br&gt;
Case study: &lt;a href="https://hygraph.com/case-studies/biocentury-case-study" rel="noopener noreferrer"&gt;Biocentury uses Content Federation to bring live data points&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

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

&lt;p&gt;Content velocity, content experimentation, standardized data, and flexible site building are cornerstones to crafting a strong brand image and a strong business. If any one of those pillars is weak, the entire system can crumble. You can gain standardized data by forcing your team into a monolithic approach, but content velocity and experimentation will suffer.&lt;/p&gt;

&lt;p&gt;By allowing your team to work in the space they’re most comfortable or where the data is best served, you open up a higher level of velocity and experimentation. With the advent of Content Federation, you don’t suffer from bad data or problematic site-building. One standardized API from customized, efficient, flexible sources.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cms</category>
      <category>architecture</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Jamstack is meaningless</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Fri, 11 Aug 2023 16:53:00 +0000</pubDate>
      <link>https://dev.to/brob/jamstack-is-meaningless-12l8</link>
      <guid>https://dev.to/brob/jamstack-is-meaningless-12l8</guid>
      <description>&lt;p&gt;Right at the top, let’s say what’s in the title… The name Jamstack is meaningless… but then again, so is MACH … and composable… and headless… and Developer experience… and serverless…, but also, they’re all hugely important&lt;/p&gt;

&lt;p&gt;Meaningless and hugely important seem to be almost diametrically opposed, so let me take a step back and explain&lt;/p&gt;

&lt;p&gt;I’ll start where all good tech thought leadership should start: With the works of Ludwig Wittgenstein.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is Wittgenstein and why do I care?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Ludwig_Wittgenstein" rel="noopener noreferrer"&gt;Wittgenstein&lt;/a&gt; was an Austrian philosopher of the early 20th century. His primary focuses were logic and philosophies of language and the mind. He had only one book published during his life: Tractatus Logico-Philosophicus.&lt;/p&gt;

&lt;p&gt;The Tractatus is a collection of 525 declarative statements that seems to try to solve both philosophy and language. needless to say, we’re not going to dive into all those, we’re going to focus on one interesting statement made near the end of the work.&lt;/p&gt;

&lt;p&gt;Often referred to as “&lt;a href="https://en.wikipedia.org/wiki/Wittgenstein%27s_ladder" rel="noopener noreferrer"&gt;Wittgenstein’s ladder&lt;/a&gt;” proposition 6.54 says the following&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“My propositions serve as elucidations in the following way: anyone who understands me eventually recognizes them as nonsensical, when he has used them—as steps—to climb beyond them. (He must, so to speak, throw away the ladder after he has climbed up it.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He must transcend these propositions, and then he will see the world aright.”&lt;/p&gt;

&lt;p&gt;In other words, as you read and understand the work of the Tractatus, you realize the words and propositions you’ve read are meaningless, despite allowing you to attain a new level of understanding: a ladder to be kicked away after you’ve climbed it&lt;/p&gt;

&lt;p&gt;And that’s where we come back to our technologies.&lt;/p&gt;

&lt;p&gt;I’ve been professionally working on the web since 2006 and building on the web since well before that. I’ve seen the eras of the web. They’re all marked by their own best practices and nomenclature. Marked by their own frameworks, both mental and code.&lt;/p&gt;

&lt;p&gt;I started with pure HTML. I grew into proto-static site generators like movable type. I got &lt;strong&gt;HYPED&lt;/strong&gt; for WordPress. Got &lt;strong&gt;Hyped&lt;/strong&gt; again for Django. Full-stack frameworks, frontend frameworks, static sites, object-oriented programming, test-driven development, domain-driven development, LAMP stack, headless… JAMstack…no, i mean Jamstack.&lt;/p&gt;

&lt;p&gt;All these terms. All this language. It’s representational of ideas of how to build sites or apps or software. They describe process. They give categorization. They build mental models. They all have common communication. They allow humans to understand technology, and humans to understand each other. And once a person (or industry) understands what the term is representing and transcends the terms to “see the world aright,” then those terms become meaningless. They have served their purpose and can be kicked away.&lt;/p&gt;

&lt;p&gt;That’s maybe where we stand with the Jamstack.&lt;/p&gt;

&lt;p&gt;The term and technologies caused a small revolution, but perhaps that term has served its purpose and now we all think of it as meaningless.&lt;/p&gt;

&lt;p&gt;Let’s take a quick aside and exam the path the Jamstack has gone through as a term and trend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jamstack origins
&lt;/h2&gt;

&lt;p&gt;We’ll start with something that, in hindsight is obvious: Jamstack is a marketing term cooked up by the folks at Netlify. Mat Biilman, co-founder of Netlify gave a &lt;a href="https://vimeo.com/163522126" rel="noopener noreferrer"&gt;talk at SmashingConf&lt;/a&gt; in 2016 (2016 was 7 years ago.. god I’m old…). Netlify was betting on the new architecture that was starting to spring up around the industry. What started as Static sites with generators like Jekyll, but went beyond “static”.&lt;/p&gt;

&lt;p&gt;They had to find a way to make static sites cool. Make them seem more than what the term static implies. Mat goes so far in the presentation to say that the word static is a bad word since the experiences we created with these tools could be incredibly dynamic.&lt;/p&gt;

&lt;p&gt;The term comes from an industry convention of naming our “technology stacks” using an acronym to describe each piece of the technology. The most famous of these at the time would have been the LAMP stack. This stood for Linux the OS, Apache, the web server, MySQL, the database, and PHP, the server-side language. There wasn’t a singular analog in the world of static sites.&lt;/p&gt;

&lt;p&gt;You could host your HTML wherever. Your data could just be markdown or pulled from postgres… or mongo db or mysql. The underlying technologies mattered less. But how you added interactivity was JavaScript and APIs and the initial page load was just Markup. JavaScript, APIs, and Markup. JAM.&lt;/p&gt;

&lt;p&gt;This sort of work was already in the zeitgeist. React and Vue had hit their first real stride. People saw how interactive the web could be and how the tools made that easier. This was already happening, but naming the movement made things catch faster and helped rally more folks to the cause.&lt;/p&gt;

&lt;p&gt;In many ways, this was an instance where culture changed the language, but then language helped change the culture. We had our ladder to help people climb to this new level of architecture.&lt;/p&gt;

&lt;p&gt;But because the JAM of the Jamstack could be nearly anything, the freeform nature of the name meant people brought their own understandings&lt;/p&gt;

&lt;h2&gt;
  
  
  Language games: Definitions matter
&lt;/h2&gt;

&lt;p&gt;Permit me another philosophical moment. Wittgenstein’s second published work (published after his death) dealt again with language. In the book, Philosophical Investigations, he explores the idea of language as a game and how the context of language is as important as syntax and vocabulary.&lt;/p&gt;

&lt;p&gt;Imagine, he argues, you hear the utterance “Water!” what this means is dependent on the context. It could be an order, as in “Fetch me water,” an answer to a question, “Yes, that is a cup of water,” or perhaps an exclamation of relief to a parched person. We can’t know without a common context.&lt;/p&gt;

&lt;p&gt;In the same way, we need comprehensive definitions to avoid miscommunications.&lt;/p&gt;

&lt;p&gt;For over two years, I ran a podcast called That’s My Jamstack. on the podcast, we talked about various technologies, and I would ask people what their entry point was into the ecosystem and their favorite parts (what was their jam in the jamstack). This always elicited interesting conversations, but I noticed (especially through the first year of hosting) that the Jamstack typically fell into two different camps… both thinking of what they were doing as the Jamstack, often with little thought to the other methodology.&lt;/p&gt;

&lt;p&gt;One camp pre-rendered their HTML via generators like Jekyll, Hugo and 11ty. The other camp took the JavaScript of the JAM stack to whole new levels and rendered everything via client-side JS with the top framework of the time: Gatsby. Sending a simple HTML wrapper over the wire and hydrating the rest via the client JS. Now, near what is being considered the end of the Jamstack era, we’ve got new technologies that have been grouped in: server-rendered (maybe more precisely “serverless-rendered) pages. Next.js, with their adoption of React Server Components, is at the forefront of this. It’s definitely a fun way of developing, but in the waning moments of the Jamstack movement, it has ALSO been considered Jamstack.&lt;/p&gt;

&lt;p&gt;Can you believe it? This is often a server-rendered application (albeit with great caching and some really great affordances to prerender when possible), and it’s considered Jamstack. In fact, beyond that, in 2022’s Jamstack survey, there are data points for RedwoodJS and Remix. Both amazing tools. Neither would be what I would consider “Jamstack” personally. Except… there they both are.&lt;/p&gt;

&lt;p&gt;And that’s because… well the definition has never really been set in stone (even with a definition listed on &lt;a href="http://jamstack.org" rel="noopener noreferrer"&gt;jamstack.org&lt;/a&gt;), and it’s been ever changing (did you know the definition on &lt;a href="http://jamstack.org" rel="noopener noreferrer"&gt;jamstack.org&lt;/a&gt; changed?).&lt;/p&gt;

&lt;p&gt;In this way, Jamstack has often been “Water!” from our earlier language game. It changes based on context, the people involved, the use case… and much much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Discourse: The Jamstack is dead
&lt;/h2&gt;

&lt;p&gt;With recent developments coming from Netlify (such as the shutting down of the Jamstack Discord community) and companies like Vercel doubling down on Next’s next iteration, the discourse has of course turned back to the term Jamstack. Is it helpful? Will it continue to exist? Is it dead? Is it toast?&lt;/p&gt;

&lt;p&gt;The general consensus has been that it doesn’t matter. Whether that’s been in thevein of “it’s served its purpose” or “did it ever really matter?” what we see is that these terms are incredibly fluid.&lt;/p&gt;

&lt;p&gt;I’d add to the discourse that while the Jamstack may be dead (and I truly mourn for that), these concepts must remain.&lt;/p&gt;

&lt;p&gt;The problem is that without a word or a flag or a rallying cry, these systems and setups will drift further apart. We’ve already seen that as “Composability” becomes the next big rallying cry. When you’re more interested in the systems that tie things together, the implementation details can become more varied. We’ll get more server-side rendering; we’ll get more static site generation; we’ll get more islands architecture. As we get all those, the overall naming convention for “modern web practices” will slip away, and the community that was found may also.&lt;/p&gt;

&lt;p&gt;I still might argue that the term Jamstack should have a place in our nomenclature, but maybe that place should be closer to its roots: static sites with interactivity brought in via small amounts of serverless functions and small amounts of front-end JavaScript.&lt;/p&gt;

&lt;p&gt;That won’t happen, of course. The Jamstack term was coined by Netlify, and as Netlify shifts toward its next horizon of composability and enterprise, the term will set over the horizon behind them.&lt;/p&gt;

&lt;p&gt;But it certainly would be nice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional reading
&lt;/h2&gt;

&lt;p&gt;In the time since I started writing this, there have been a lot of great articles near this subject. Here are a few of my favorites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://remotesynthesis.com/blog/goodbye-jamstack/" rel="noopener noreferrer"&gt;Brian Rinaldi kicked things off&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.swyx.io/netlify-era-jamstack-end" rel="noopener noreferrer"&gt;Swyx always provides great insight&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thenewstack.io/is-jamstack-toast-some-developers-say-yes-netlify-says-no/" rel="noopener noreferrer"&gt;Props for The New Stack using "toast" in their headline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jamstack</category>
      <category>dev</category>
      <category>architecture</category>
      <category>javascript</category>
    </item>
    <item>
      <title>😱 Book Release: Eleventy by Example – Learn 11ty with 5 in-depth projects</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Thu, 04 May 2023 13:06:00 +0000</pubDate>
      <link>https://dev.to/brob/book-release-eleventy-by-example-learn-11ty-with-5-in-depth-projects-54hk</link>
      <guid>https://dev.to/brob/book-release-eleventy-by-example-learn-11ty-with-5-in-depth-projects-54hk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17y4lne7k767w2sj69ve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17y4lne7k767w2sj69ve.png" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh hi, everyone! Listen, I've got some really exciting news. I'm honestly kind of shocked. I wrote a book. For a publisher. That comes out tomorrow.&lt;/p&gt;

&lt;p&gt;Amazing right?&lt;/p&gt;

&lt;p&gt;Well, listen, it's a book that talks about my absolute favorite development topic: 11ty.&lt;/p&gt;

&lt;p&gt;Let's not go any farther without a link to buy... that seems appropriate, right? It's &lt;a href="https://packt.link/iaoYc" rel="noopener noreferrer"&gt;available in paperback and eBook&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's covered?
&lt;/h2&gt;

&lt;p&gt;Well... a lot. We build five distinct projects to cover all the basics, intermediate, and some advanced topics. Let's do a quick rundown of projects and skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project 1: Basic Marketing website
&lt;/h3&gt;

&lt;p&gt;The first project is super basic. We take some simple HTML and we build out a website with 11ty. To do this, we tackle a few topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic project setup&lt;/li&gt;
&lt;li&gt;Running with no configuration&lt;/li&gt;
&lt;li&gt;Best practice directory structure and configuration&lt;/li&gt;
&lt;li&gt;Page templates, layouts, and includes&lt;/li&gt;
&lt;li&gt;The data cascade and data in templates

&lt;ul&gt;
&lt;li&gt;Global data, template data, JavaScript data, you name it, we got it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Deployment to Netlify and CloudFlare&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Not bad for a simple marketing site tutorial!&lt;/p&gt;

&lt;h3&gt;
  
  
  Project 2: Simple blog
&lt;/h3&gt;

&lt;p&gt;Who doesn't need a blog example, am I right? In this project, we create a blog with 11ty including dealing with dreaded off-by-one date issues. The solution I wrote for the book is now &lt;a href="https://www.11ty.dev/docs/dates/#also-on-youtube" rel="noopener noreferrer"&gt;one of the recommended solutions in 11ty's docs&lt;/a&gt;. We also make our blogs more interesting with custom media types.&lt;/p&gt;

&lt;p&gt;Skills learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collections&lt;/li&gt;
&lt;li&gt;Custom page templates&lt;/li&gt;
&lt;li&gt;Pagination (powerful, but confusing to new devs)&lt;/li&gt;
&lt;li&gt;Custom shortcodes (embed YouTube, CodePen, and blockquotes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project 3: Photography site
&lt;/h3&gt;

&lt;p&gt;We use what we learned in the blog project and create a custom photography site to play with the Image plugin. Honestly, this was the project that took me the most work. The Image plugin is great, but I'd never used it, and dealing with responsive images in the browser takes some getting used to.&lt;/p&gt;

&lt;p&gt;Skills learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing and configuring a plugin&lt;/li&gt;
&lt;li&gt;Using the 11ty Image plugin&lt;/li&gt;
&lt;li&gt;Responsive images&lt;/li&gt;
&lt;li&gt;Custom data based on the file system (another really clever code snippet that I'm quite proud of to get a list of images as data for each post)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project 4: Podcast website
&lt;/h3&gt;

&lt;p&gt;While not everyone has a podcast, most people know about podcasts. In this project, we tackle some of the interesting requirements for dealing with podcasts. We then use this project as a base to talk about integrating with 3rd parties: Hygraph (headless CMS) and Algolia (Search).&lt;/p&gt;

&lt;p&gt;Skills learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom filters&lt;/li&gt;
&lt;li&gt;Custom file types (exporting JSON and RSS instead of HTML)&lt;/li&gt;
&lt;li&gt;Using plugins&lt;/li&gt;
&lt;li&gt;11ty Serverless for search and indexing&lt;/li&gt;
&lt;li&gt;JavaScript data files to fetch data from a headless CMS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project 5: A custom plugin
&lt;/h3&gt;

&lt;p&gt;Finally, we use everything we've learned to put together a custom plugin to have all our shortcodes, filters and more readily available for any new project.&lt;/p&gt;

&lt;p&gt;Skills Learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugin setup&lt;/li&gt;
&lt;li&gt;Plugin testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ready to learn?
&lt;/h2&gt;

&lt;p&gt;I'm really proud of the book and everything it teaches. If you're interested in picking up 11ty, I think it's a great resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packt.link/iaoYc" rel="noopener noreferrer"&gt;You can order it today as an ebook or print book&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>static</category>
      <category>jamstack</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>11ty Second 11ty: Creating Template Filters</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Mon, 15 Aug 2022 13:30:00 +0000</pubDate>
      <link>https://dev.to/brob/11ty-second-11ty-creating-template-filters-40k1</link>
      <guid>https://dev.to/brob/11ty-second-11ty-creating-template-filters-40k1</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/HAqiDnymJP8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;11ty Template filters are ways of manipulating data in a variable tag in your template language of choice. Each template language brings it’s own set and 11ty adds a few defaults, as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a built-in filter
&lt;/h2&gt;

&lt;p&gt;To use a built-in filter, write a variable or expression in your language of choice, here’s a liquid template that will display the string “hi there” on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Filter Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ "hi there" }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have that, add the pipe character and the filter’s name. In this case, the built-in slugify filter will convert the string to a url-appropriate string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Filter Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ "hi there" | slugify }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Outputs: &amp;lt;h2&amp;gt;hi-there&amp;lt;/h2&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making custom filters
&lt;/h2&gt;

&lt;p&gt;Next to make a custom filter, we need to have a configuration file. We’ll create &lt;code&gt;.eleventy.js&lt;/code&gt; in our project root.&lt;/p&gt;

&lt;p&gt;From there, we’ll export out a function that has the &lt;code&gt;eleventyConfig&lt;/code&gt; object and use the &lt;code&gt;addFilter&lt;/code&gt; method on that object.&lt;/p&gt;

&lt;p&gt;The method takes two properties, a name for the filter for use in the template, and a function that will be used to mutate the data.&lt;/p&gt;

&lt;p&gt;In this case, we’ll make an uppercase filter to convert our string to all uppercase. The function we’ll make takes the first argument of the string the filter is applied to. In this case, we’ll use &lt;code&gt;string.toUpperCase()&lt;/code&gt; in Javascript to return the mutated string.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uppercase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;Then we can use this in the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Filter Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ "hi there" | uppercase }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also adjust markup around your string, as well. In this case, we’ll take the &lt;em&gt;bad idea&lt;/em&gt; of adjusting the string’s color in our filter. Any argument after the first in our filter will be the options that can be passed to the filter. In this case a color string. We can wrap the string in a span and set the color inline.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uppercase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&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="s2"&gt;`&amp;lt;span style="color: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&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;Then we can use this in the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Filter Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ "hi there" | color: "red" }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And more!
&lt;/h2&gt;

&lt;p&gt;Filters can be a super powerful piece of your 11ty builds. They allow you to mutate data on the fly no matter the source. Anything you can do in Javascript, you can do in a filter, so play around with things like creating a date string filter for Nunjucks (only available in Liquid) or reordering array or creating a strong set of HTML templates for common use cases in your markup or markdown.&lt;/p&gt;

</description>
      <category>11ty</category>
      <category>jamstack</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>11ty Second 11ty: Global Data files (JS and JSON)</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Mon, 08 Aug 2022 17:22:00 +0000</pubDate>
      <link>https://dev.to/brob/11ty-second-11ty-global-data-files-js-and-json-566o</link>
      <guid>https://dev.to/brob/11ty-second-11ty-global-data-files-js-and-json-566o</guid>
      <description>&lt;p&gt;In these two videos, we take 110 seconds each to discuss how to use 11ty's Global Data files to pull static data with JSON and dynamic (at build time) data with JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  11ty JSON Data Files
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/x8jcqvsC4cY"&gt;
&lt;/iframe&gt;
&lt;br&gt;
In this episode, we’re going to talk about Global data files. Specifically JSON data files. So, 110 seconds on the clock, Let’s go!&lt;/p&gt;

&lt;p&gt;11ty has a concept of &lt;a href="https://www.11ty.dev/docs/data-cascade/" rel="noopener noreferrer"&gt;the data cascade&lt;/a&gt;, and while we won’t go into details on that in this video, we do need to mention that global data files are the lowest priority, so if you set something in a global data file and change it using the configuration API, the API will overwrite that data&lt;/p&gt;

&lt;p&gt;To get started, we have a barebones 11ty site. All we have is an index file. In order to add global data, we need a directory for our files to live. The default is an &lt;code&gt;_data&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We’ll create that and a json data file. The name of the file will be the key we’ll use to access the data in our templates. In this case, we'll name it &lt;code&gt;siteSetting.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’ll make a super simple site settings data file. Global information we may want in multiple templates later on.&lt;/p&gt;

&lt;p&gt;Then, we’ll put a JSON object here with a few pieces of data. We’re using an object here, but any valid JSON will work including an array.&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="err"&gt;“name”:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;“Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Example”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;“description”:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;“A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;site&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="err"&gt;ty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="err"&gt;ty”&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;Once this is saved in, we can access it from our index template (or any other template... it's global, right?).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Document&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Data Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    {{ siteSetting.name }}
    {{ siteSetting.description }}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Content at Build time with 11ty JS Data Files
&lt;/h2&gt;

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

&lt;p&gt;Now this works for static content, but what if we want to add some dynamic content per build… say a date for the copyright in our footer.&lt;/p&gt;

&lt;p&gt;We can do that with a Javascript Data file&lt;/p&gt;

&lt;p&gt;Let’s copy the &lt;code&gt;siteSetting.json&lt;/code&gt; file and move it to a &lt;code&gt;settings.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;From here, we’ll reformat to use &lt;code&gt;module.exports&lt;/code&gt; and export this object&lt;/p&gt;

&lt;p&gt;Now, we’ll add a simple copyright property using JavaScript’s built in Date method. We’ll generate the full date time and format it in our template.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Settings for the bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we add this to our &lt;code&gt;index.html&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;11ty Second 11ty: Data Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  {{ siteSetting.name }}
  {{ siteSetting.description }}

  {{ settings.date | date: "%Y" }}

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The date is fine, but what if you want to hit another API and get content? JS data files work well for simple cases of this, as well.&lt;/p&gt;

&lt;p&gt;First, we’ll make a new data file: &lt;code&gt;pokemon.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In here, we’ll make an API call to the PokeAPI which is a great simple API for learning purposes. In this case, we’ll use Axios (make sure it’s installed), but node-fetch or the latest node built-in fetch would work as well.&lt;/p&gt;

&lt;p&gt;We export a function this time and return an array of items we wish to access. In this case. The response has a &lt;code&gt;data&lt;/code&gt; object with a &lt;code&gt;results&lt;/code&gt; array&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// pokemon api call&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://pokeapi.co/api/v2/pokemon/?limit=151&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;pokemon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;results&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;pokemon&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
  {% for pokemon in pokemon %}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{ pokemon.name }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  {% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are just the basics of using data files. Anything you can do in JSON or javascript, you can do in these files, including arrays, nesting, making calls to external APIs, transforming data and more.&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>11ty</category>
      <category>javascript</category>
    </item>
    <item>
      <title>11ty second 11ty: The Render Plugin Part 1</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Mon, 20 Jun 2022 14:30:00 +0000</pubDate>
      <link>https://dev.to/brob/11ty-second-11ty-the-render-plugin-part-1-4p9m</link>
      <guid>https://dev.to/brob/11ty-second-11ty-the-render-plugin-part-1-4p9m</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/AXyedHIE7Fc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The Render plugin is comprised of two shortcodes for use in your Nunjucks, Liquid or JS templates. It’s a plugin that is bundled with the main 11ty NPM package and ready to use as soon as you nom install 11ty.&lt;/p&gt;

&lt;p&gt;To get started with the plugin, you need to install it in your &lt;code&gt;.eleventy.js&lt;/code&gt; config file by requiring the plugin and then initializing it&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EleventyRenderPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@11ty/eleventy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EleventyRenderPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two shortcodes it adds to your project are &lt;code&gt;renderTemplate&lt;/code&gt; and &lt;code&gt;renderFile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this part, we’ll tackle &lt;code&gt;renderTemplate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;renderTemplate&lt;/code&gt; allows you to put a string between two matching shortcodes and render that string in a templating language different from that of the current template&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the &lt;code&gt;renderTemplate&lt;/code&gt; tag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ...head, etc. --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The date is: {{ today | date: "%B %d, %y" }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have a Nunjucks index page. In this page, we have a date time. It’s not great to read for a human. I want to reformat that in the template with a filter, but uh-oh, Nunjucks doesn’t have a date filter! Now that my project has the renderTemplate paired shortcode, I can get to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% renderTemplate "liquid", settings %} 
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The date is: {{ today | date: "%B %d, %y" }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
{% endrenderTemplate %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The renderTemplate tag accepts two arguments: the templating language we wish to use (any &lt;a href="https://www.11ty.dev/docs/config/#template-formats" rel="noopener noreferrer"&gt;template string&lt;/a&gt; that eleventyConfig accepts will work such as &lt;code&gt;html,liquid,ejs,md,hbs,mustache,haml,pug,njk,11ty.js&lt;/code&gt;) and an optional set of data.&lt;/p&gt;

&lt;p&gt;In this case, I want to render the string with Liquid and I want to get a variable off the settings global JavaScript data file that is generating the current date.&lt;/p&gt;

&lt;p&gt;Anything between the &lt;code&gt;renderTemplate&lt;/code&gt; and the &lt;code&gt;endRenderTemplate&lt;/code&gt; will be rendered in Liquid instead of Nunjucks.&lt;/p&gt;

&lt;p&gt;This allows me to use the powerful date filter in liquid to format the date time string create by a JS data file (named settings.js).&lt;/p&gt;

&lt;p&gt;Or for a simpler use case, sometimes writing Markdown is more ergonomic than writing HTML, so why not embed a little Markdown in your HTML&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% renderTemplate "liquid,md" %}
# I am a title

* I am a list
* I am a list

1. I am an ordered list
1. i'm actually second in an ordered list test
{% endrenderTemplate %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Template engines all the way down!
&lt;/h2&gt;

&lt;p&gt;This plugin opens the door to a lot of new possibilities. Any templating language can be embedded in any page or template! 11ty: It's templates all the way down!&lt;/p&gt;

&lt;p&gt;Just want the code? Check the &lt;a href="https://github.com/brob/11ty-second-11ty/tree/main/render" rel="noopener noreferrer"&gt;11ty Second 11ty repo&lt;/a&gt; for this and others!&lt;/p&gt;

</description>
      <category>11ty</category>
      <category>javascript</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>11ty Second 11ty: Basics of Collections</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Mon, 13 Jun 2022 20:52:45 +0000</pubDate>
      <link>https://dev.to/brob/11ty-second-11ty-basics-of-collections-286f</link>
      <guid>https://dev.to/brob/11ty-second-11ty-basics-of-collections-286f</guid>
      <description>&lt;p&gt;Collections are the workhorse of most 11ty projects. They provide a way of grouping content or data in meaningful ways. By default, this is done by providing a &lt;code&gt;tag&lt;/code&gt; property to a piece of content (such as in the front matter of your markdown)&lt;/p&gt;

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

</description>
      <category>11ty</category>
      <category>jamstack</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating a better date-picking experience with the DateRangePicker component</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Thu, 13 Jan 2022 22:15:11 +0000</pubDate>
      <link>https://dev.to/algolia/creating-a-better-date-picking-experience-with-the-daterangepicker-component-4019</link>
      <guid>https://dev.to/algolia/creating-a-better-date-picking-experience-with-the-daterangepicker-component-4019</guid>
      <description>&lt;p&gt;When crafting a search interface, it's often enough to use what comes prepackaged with Algolia's InstantSearch libraries. Each library comes in a different flavor of JavaScript for your ease of use. Each one has all the main pieces you need to craft a strong UI. &lt;/p&gt;

&lt;p&gt;Sometimes you want to go beyond what's available. &lt;/p&gt;

&lt;p&gt;At that point, you're presented with two options: craft your own with our connector APIs or find a pre-built custom widget.&lt;/p&gt;

&lt;p&gt;It should come as no surprise to anyone that knows me that when presented with wanting a React date picker for &lt;a href="https://www.algolia.com/blog/engineering/algolia-coding-challenge-help-santa/" rel="noopener noreferrer"&gt;December's coding challenge,&lt;/a&gt; I wanted to write as little code as possible to make it work, but I also wanted the best UI possible for Santa. When it came to bonus challenge number 5, I needed something extra. My first stop? &lt;a href="https://www.algolia.com/developers/code-exchange/" rel="noopener noreferrer"&gt;The Algolia Code Exchange&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;After a quick search around &lt;code&gt;date&lt;/code&gt; components, I found exactly what I needed: the aptly-named &lt;a href="https://github.com/algolia/react-instantsearch-widget-date-range-picker" rel="noopener noreferrer"&gt;@algolia/react-instantsearch-widget-date-range-picker&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;If you'd like to follow along, you'll need to install a new React InstantSearch project. The easiest way to do that is to run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; npx create-instantsearch-app concert-search &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--app-id&lt;/span&gt; latency &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--api-key&lt;/span&gt; 059c79ddd276568e990286944276464a &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--index-name&lt;/span&gt; concert_events_instantsearchjs &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s2"&gt;"React InstantSearch"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This monster command will set up a React InstantSearch project and connect it to &lt;a href="https://github.com/algolia/datasets/tree/master/concerts" rel="noopener noreferrer"&gt;Algolia's hosted concert Index&lt;/a&gt;. Change directory into the created folder and run &lt;code&gt;yarn start&lt;/code&gt; for a solid starting point.&lt;/p&gt;

&lt;p&gt;At this point, you've got a full search experience for a set of concerts. &lt;/p&gt;

&lt;p&gt;If you want to filter by date, you could set up a &lt;code&gt;&amp;lt;RefinementList&amp;gt;&lt;/code&gt; component and set its &lt;code&gt;attribute&lt;/code&gt; prop to &lt;code&gt;date&lt;/code&gt;, but those dates are Unix timestamps (for easiest comparisons). This isn't ideal for the user experience. Let's make that better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and configuring the widget
&lt;/h2&gt;

&lt;p&gt;To get the date range picker up and running, we need to install a couple of dependencies.&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @algolia/react-instantsearch-widget-date-range-picker @duetds/date-picker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will install the official Algolia React Date picker widget and its dependency the Duet Date picker.&lt;/p&gt;

&lt;p&gt;From here, we need to import the packages into our &lt;code&gt;src/App.js&lt;/code&gt; file and initialize the Duet Date Picker for use.&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;// Add right after the imports included in the create-instantsearch-app code&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;DateRangePicker&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;@algolia/react-instantsearch-widget-date-range-picker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineCustomElements&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;@duetds/date-picker/dist/loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Defines the custom elements from the date picker for use on the window object  &lt;/span&gt;
&lt;span class="nf"&gt;defineCustomElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that these packages are imported, we're ready to get this on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the date range picker
&lt;/h2&gt;

&lt;p&gt;To add the picker to the page, we need to select a spot within the &lt;code&gt;&amp;lt;InstantSearch&amp;gt;&lt;/code&gt; component. The base of the app is a &lt;code&gt;search-panel&lt;/code&gt;. By default, all we have inside this is the results, but we can add a filter panel, as well.&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;InstantSearch&lt;/span&gt; &lt;span class="nx"&gt;_searchClient_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;searchClient&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;concert_events_instantsearchjs&lt;/span&gt;&lt;span class="dl"&gt;"&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;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search-panel&lt;/span&gt;&lt;span class="dl"&gt;"&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;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search-panel__filters&lt;/span&gt;&lt;span class="dl"&gt;"&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;DateRangePicker&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&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="sr"&gt;/div&amp;gt; &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;_className_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search-panel__results&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//... Results code&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&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;/div&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;/InstantSearch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DateRangePicker accepts an &lt;code&gt;attribute&lt;/code&gt; prop. This prop accepts a date-based attribute from the hits in our Index. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Quick note&lt;/em&gt;: The DateRangePicker accepts a Unix timestamp with milliseconds since the Epoch, not seconds. Depending on your data structure, you may need to create a secondary timestamp in your data.&lt;/p&gt;

&lt;p&gt;When you view the rendered page, you should now have a date picker. There are a few small UI snags to clean up. &lt;/p&gt;

&lt;p&gt;The Duet Date picker uses a slew of CSS Custom Properties for much of its styling. At the start of our &lt;code&gt;src/App.css&lt;/code&gt; file, we need to paste those in and configure for our app (as appropriate).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3c4ee0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-text-active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#666&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f5f5f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-color-border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d6d6e7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

  &lt;span class="py"&gt;--duet-font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;'Segoe UI'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-font-normal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-font-bold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

  &lt;span class="py"&gt;--duet-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;--duet-z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The two picker areas are a little close together still, so let's fix that with a little CSS, as well. This can go in &lt;code&gt;src/App.css&lt;/code&gt; but should be closer to the bottom. There are many ways to get the space between the two items, the quick and easy solution is to use CSS Grid and the &lt;code&gt;gap&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nc"&gt;.date-range-picker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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;And with that, we have a functioning, user-friendly date picker. No more dealing with Unix timestamps and conversions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take this further
&lt;/h2&gt;

&lt;p&gt;This works great in the small sample dataset, but can also be brought into any Algolia InstantSearch application. If you're looking to take this example further, create a better UI around each Hit by editing the hit component in &lt;code&gt;src/App.js&lt;/code&gt;. You could also show the current filters applied with the &lt;a href="https://www.algolia.com/doc/api-reference/widgets/current-refinements/react/" rel="noopener noreferrer"&gt;CurrentRefinements component&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a GitHub Issue Recommendation Bot with Algolia</title>
      <dc:creator>Bryan Robinson</dc:creator>
      <pubDate>Thu, 02 Dec 2021 18:21:18 +0000</pubDate>
      <link>https://dev.to/algolia/building-a-github-issue-recommendation-bot-3foo</link>
      <guid>https://dev.to/algolia/building-a-github-issue-recommendation-bot-3foo</guid>
      <description>&lt;p&gt;GitHub Issues are static content. What if they didn't have to be?&lt;/p&gt;

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

&lt;p&gt;When we (DevRels Chuck Meyer and Bryan Robinson) discovered that &lt;a href="https://dev.to/devteam/join-us-for-the-2021-github-actions-hackathon-on-dev-4hn4"&gt;Dev.to was hosting a GitHub Actions hackathon&lt;/a&gt;, we knew we needed to give it a try. &lt;/p&gt;

&lt;p&gt;We knew we wanted to figure out a useful tool to integrate Algolia into an Action. There were obvious thoughts on what sort of project to undertake. We thought through the usual takes on indexing content, products, or markdown. They all would have been helpful for web creators. Would they have been helpful to open source maintainers, though? Probably?&lt;/p&gt;

&lt;p&gt;How could we make their overall workflow better?&lt;/p&gt;

&lt;p&gt;Then it struck us: What if we could give recommended Issues for frequently asked questions? Could we lessen the burden of maintainers answering similar questions? How many Issues get closed as "duplicate" in large repositories? Could Algolia provide Issue creators a list of related, helpful Issues?&lt;/p&gt;

&lt;p&gt;Spoiler alert: Yeah, totally!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow structure
&lt;/h2&gt;

&lt;p&gt;When a developer adds an Issue to a repository, we need to execute three steps.&lt;/p&gt;

&lt;p&gt;First, we need to search an Algolia Index for related Issues. Then, we bundle those results into Markdown and pass it to an Action to create a comment on the initial Issue. Finally, we need to put the Issue into our Index for future searches.&lt;/p&gt;

&lt;p&gt;Each of these steps requires an Action. The Algolia-specific Actions, we needed to create from scratch. The comment-writing Action, we decided to use the amazing Peter Evan's &lt;a href="https://github.com/marketplace/actions/create-or-update-comment" rel="noopener noreferrer"&gt;create-or-update-comment Action&lt;/a&gt; – which, as it turns out, GitHub uses in many of their docs about Actions.&lt;/p&gt;

&lt;p&gt;Let's dive into the new Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performing a search query
&lt;/h2&gt;

&lt;p&gt;The first step of our Workflow is a search query sent to Algolia. We created a custom Action for this (&lt;a href="https://github.com/marketplace/actions/get-algolia-issue-records" rel="noopener noreferrer"&gt;Get Algolia Issue Records&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;To use the Action, we need to send it four required inputs (and an optional fifth).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;app_id&lt;/code&gt;: the ID of the application in your Algolia account. This is best stored as a Secret in your repository&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;api_key&lt;/code&gt;: An API key with search permissions to the Index in your Algolia App. This is best stored in a Secret in your repository.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;index_name&lt;/code&gt;: The name of the Algolia Index to search. For consistency, we recommend the &lt;code&gt;github.event.repository.name&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;issue_title&lt;/code&gt;: The title of the inciting Issue found with &lt;code&gt;github.event.issue.title&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;max_results&lt;/code&gt;: (OPTIONAL) A number of results to return to the comment (defaults to 3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We take these variables and perform a &lt;code&gt;similarQuery&lt;/code&gt; search based on the inciting Issue's title. We then create a comment body and a list of items in Markdown (the format needed for GitHub comments). This outputs are passed to &lt;a href="https://github.com/marketplace/actions/create-or-update-comment" rel="noopener noreferrer"&gt;Peter Evans' create-or-update-comment Action&lt;/a&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inspect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;util&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;core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@actions/core&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;algoliasearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;algoliasearch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;issueTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;issue_title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;maxResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;max_results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Inputs: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing one or more of Algolia app id, API key, or index name.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxResults&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;algoliasearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;similarQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hitsPerPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxResults&lt;/span&gt;
      &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Searching for record`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hits: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hits&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`## Other issues similar to this one:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hits&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;hit&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;hit&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="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&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="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listItems&lt;/span&gt; &lt;span class="o"&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;hits&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;hit&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;hit&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="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&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="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;comment_body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;issues_list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Resource not accessible by integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`See this action's readme for details about this error`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Adding the issue to your Algolia index
&lt;/h2&gt;

&lt;p&gt;For the final step of our workflow, we add this new issue to the Algolia index for future searches. We created another GitHub Action for this purpose: &lt;a href="https://github.com/marketplace/actions/create-or-update-algolia-index-record" rel="noopener noreferrer"&gt;Create or Update Algolia Index Record&lt;/a&gt;. This action atomically adds/updates a record directly to an index rather than writing/reading from a JSON file. This makes sense in situations where we are acting on metadata about the repo (issues, pull requests, comments) as opposed to building an index for the application itself.&lt;/p&gt;

&lt;p&gt;To use this action, we'll need to &lt;a href="https://www.algolia.com/doc/guides/security/api-keys/#creating-and-managing-api-keys" rel="noopener noreferrer"&gt;create an Algolia API key&lt;/a&gt; with permissions to add/update records in our index. Additionally, we will need permission to create a new index for the repo. Otherwise, we must create it ahead of time and hard code the index name in our configuration.&lt;/p&gt;

&lt;p&gt;Along with the new API key we'll need a few other inputs to use the action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;app_id&lt;/code&gt;: You should already have this as a Secret in your repository from the action above&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;api_key&lt;/code&gt;: This is the new key with permission to save records to your index. This is best stored in a Secret in your repository.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;index_name&lt;/code&gt;: The name of the Algolia index to add/update this record. For consistency, we recommend the &lt;code&gt;github.event.repository.name&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;record&lt;/code&gt;: A string represneting the JSON record to add to the index.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the API key has permission, the action creates an index for the repository. We'll add the issue title and URL (to link back) as the &lt;code&gt;record&lt;/code&gt;. It's a multi-line string in our workflow, but must_ be valid JSON for the action to work (see &lt;a href="https://www.algolia.com/doc/guides/sending-and-managing-data/prepare-your-data/#algolia-records" rel="noopener noreferrer"&gt;https://www.algolia.com/doc/guides/sending-and-managing-data/prepare-your-data/#algolia-records&lt;/a&gt; for details).&lt;/p&gt;

&lt;p&gt;We take all of these inputs and execute a &lt;code&gt;saveObject&lt;/code&gt; call via the Algolia API. We use the &lt;code&gt;issue ID&lt;/code&gt; as the &lt;code&gt;objectID&lt;/code&gt; in the index. This makes it easy to tie the record back to this issue if we add workflows for update or delete events later.&lt;br&gt;
&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inspect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;util&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;core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@actions/core&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;algoliasearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;algoliasearch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;record&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Inputs: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing one or more of Algolia app id, API key, or index name.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Writing record to index &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;algoliasearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autoGenerateObjectIDIfNotExist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;objectID&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;objectID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`Created record in index &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with objectID &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;objectID&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="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to save object: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&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="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Resource not accessible by integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`See this action's readme for details about this error`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, we piece the two new Actions together with the existing comment creation action to build our workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  The full workflow file
&lt;/h2&gt;

&lt;p&gt;To make this work, we need one &lt;code&gt;job&lt;/code&gt; with three &lt;code&gt;steps&lt;/code&gt;. Each step will use one of these Actions.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;related-issues&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Triggers the workflow on push or pull request events but only for the main branch&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;opened&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;get-related-issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="c1"&gt;# Gives the workflow write permissions only in issues&lt;/span&gt;
      &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Performs a search in an Algolia Index based on Issue Title&lt;/span&gt;
      &lt;span class="c1"&gt;# The Index should have historical Issues&lt;/span&gt;
      &lt;span class="c1"&gt;# Returns two outputs:&lt;/span&gt;
      &lt;span class="c1"&gt;# issues_list: a markdown list of issues&lt;/span&gt;
      &lt;span class="c1"&gt;# comment_body: a generic comment body with the list of issues&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Search based on issue title&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;brob/algolia-issue-search@v1.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="c1"&gt;# Requires an Algolia account with an App ID and write key&lt;/span&gt;
          &lt;span class="na"&gt;app_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALGOLIA_APP_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALGOLIA_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;index_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.repository.name }}&lt;/span&gt;
          &lt;span class="na"&gt;issue_title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.issue.title }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create or Update Comment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peter-evans/create-or-update-comment@v1.4.5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# GITHUB_TOKEN or a repo scoped PAT.&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.token }}&lt;/span&gt;
          &lt;span class="c1"&gt;# The number of the issue or pull request in which to create a comment.&lt;/span&gt;
          &lt;span class="na"&gt;issue-number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.issue.number }}&lt;/span&gt;
          &lt;span class="c1"&gt;# The comment body. Can use either issues_list or comment_body&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;# While you wait, here are related issues:&lt;/span&gt;
            &lt;span class="s"&gt;${{ steps.search.outputs.issues_list }}&lt;/span&gt;
            &lt;span class="s"&gt;Thank you so much! We'll be with you shortly!&lt;/span&gt;
      &lt;span class="c1"&gt;# An Action to create a record in an Algolia Index&lt;/span&gt;
      &lt;span class="c1"&gt;# This is a generic Action and can be used outside of this workflow&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add Algolia Record&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingest&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chuckmeyer/add-algolia-record@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;app_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALGOLIA_APP_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALGOLIA_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;index_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.repository.name }}&lt;/span&gt;
          &lt;span class="c1"&gt;# Record needs to be a string of JSON&lt;/span&gt;
          &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;{&lt;/span&gt;
              &lt;span class="s"&gt;"title": "${{ github.event.issue.title }}", &lt;/span&gt;
              &lt;span class="s"&gt;"url": "${{ github.event.issue.html_url }}", &lt;/span&gt;
              &lt;span class="s"&gt;"labels": "${{ github.event.issue.labels }}",&lt;/span&gt;
              &lt;span class="s"&gt;"objectID": "${{ github.event.issue.number }}"&lt;/span&gt;
            &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;We hope that this is helpful to maintainers, but we also hope it inspires others to find better and better ways to suggest content in static areas like GitHub Issues. &lt;/p&gt;

&lt;p&gt;If you want to play around with the full workflow, you can check it out in &lt;a href="https://github.com/brob/github-issues-search-bot" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;. Both the search and ingest Actions are available in the &lt;a href="https://github.com/marketplace?type=actions&amp;amp;query=Algolia" rel="noopener noreferrer"&gt;GitHub marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Search and discovery can become an interesting part of your automated workflow in GitHub and beyond.&lt;/p&gt;

&lt;p&gt;Post and Bot by:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__680058"&gt;
    &lt;a href="/chuckm" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F680058%2F3c47b4a5-be92-44c0-9237-c7ddb7d447de.jpeg" alt="chuckm image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/chuckm"&gt;Chuck Meyer&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/chuckm"&gt;API driven. DevRel 🥑  at Algolia.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag__user ltag__user__id__173278"&gt;
    &lt;a href="/brob" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F173278%2F96678344-7d51-41ca-8ff4-78343d865e05.jpg" alt="brob image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/brob"&gt;Bryan Robinson&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/brob"&gt;I'm a designer, developer, lover of static sites and CSS&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>actionshackathon21</category>
      <category>javascript</category>
      <category>github</category>
    </item>
  </channel>
</rss>
