<?xml version="1.0" encoding="utf-8"?>
  <?xml-stylesheet href="/feed.xsl" type="text/xsl"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
      <title>Andrew Stiefel | guides</title>
      <link href="https://andrewstiefel.com/feed/guides.xml" rel="self"/>
      <link href="https://andrewstiefel.com/guides/" rel="alternate"/>
      <id>https://andrewstiefel.com/guides/</id>
      <subtitle>A feed of guides by Andrew Stiefel.</subtitle>
      <updated>2026-04-11T20:02:18-07:00</updated>
      <author>
        <name>Andrew Stiefel</name>
        <email></email>
      </author>
      <rights type="text">Copyright © 2026 {"name" => "Andrew Stiefel", "url" => "https://andrewstiefel.com", "linkedin" => "andrewstiefel", "codeberg" => "andrewstiefel"}. All rights reserved.</rights>
      <entry>
        <title>Building a Personal Monorepo for Writing</title>
        <link rel="alternate" href="https://andrewstiefel.com/monorepo/"/>
        <published>2025-07-19T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/personal-monorepo</id>
        <summary>How I created a workflow for researching and blogging with Obsidian and Jekyll</summary>
        <content type="html">&lt;p&gt;I use &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; to organize my thinking and writing. But I’ve always been dissatisfied with the process of turning what I write into a formatted post for my website. I either had to manage multiple vaults—adding complexity and violating one of my core principles of note-taking—or copy, paste, and edit my writing in another tool like VS Code.&lt;/p&gt;
          &lt;p&gt;I also prefer to keep my content separate from the website’s design, and I wanted to do that without introducing a CMS or additional tooling. Static site generators like Jekyll are great at converting Markdown into websites, but they usually require you to store content alongside all the other website files.&lt;/p&gt;
          &lt;p&gt;After some experimenting, I landed on a better solution: building a personal monorepo for my writing and publishing workflow.&lt;/p&gt;
          &lt;h2 id=&quot;why-use-a-monorepo&quot;&gt;Why use a monorepo?&lt;/h2&gt;
          &lt;p&gt;A &lt;em&gt;monorepo&lt;/em&gt;—short for monolithic repository—combines code for multiple projects into a single repository. A common pattern is to store both frontend and backend code in one place.&lt;/p&gt;
          &lt;p&gt;That’s actually a good analogy for how I think about writing. The “backend” is my Obsidian vault, where I research and take notes. The “frontend” is the website where I publish finished posts.&lt;/p&gt;
          &lt;h2 id=&quot;basic-setup&quot;&gt;Basic Setup&lt;/h2&gt;
          &lt;p&gt;To start ,I moved my Obsidian vault and Jekyll website into a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;personal-monorepo&lt;/code&gt; directory structured like this:&lt;/p&gt;
          &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/personal-monorepo
          ├── /notes
          └── /website
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;If you don’t already have an Obsidian vault or Jekyll site, you can easily replicate this from scratch. Install Obsidian, open the &lt;strong&gt;/notes&lt;/strong&gt; directory using the “Open folder as vault” option, and you’re ready to go.&lt;/p&gt;
          &lt;p&gt;Setting up Jekyll requires a bit more technical work, but &lt;a href=&quot;https://jekyllrb.com/docs/installation/&quot;&gt;you can find installation instructions here&lt;/a&gt;.&lt;/p&gt;
          &lt;h2 id=&quot;configuring-obsidian&quot;&gt;Configuring Obsidian&lt;/h2&gt;
          &lt;p&gt;For this approach it’s important to make a directory where you will only store published posts. I’m going to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/notes/posts&lt;/code&gt; for this tutorial but you can configure this directory any way you wish.&lt;/p&gt;
          &lt;p&gt;I also recommend installing two Obsidian community plugins:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://github.com/Vinzent03/obsidian-git&quot;&gt;Obsidian Git&lt;/a&gt; – adds version control and makes publishing easy&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://github.com/kepano/obsidian-permalink-opener&quot;&gt;Obsidian Permalink Opener&lt;/a&gt; – lets you open post URLs in your browser for previewing&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;For Git, you can push commits manually or set up periodic syncs. For the Permalink Opener plugin, I added both my website’s base URL and my local development URL so I can preview posts in the browser while editing.&lt;/p&gt;
          &lt;p&gt;With that we’re ready to move on to building our website.&lt;/p&gt;
          &lt;h2 id=&quot;connecting-jekyll-and-obsidian&quot;&gt;Connecting Jekyll and Obsidian&lt;/h2&gt;
          &lt;p&gt;By default, Jekyll looks for posts in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/website/_posts&lt;/code&gt;. But I want it to use the content in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/notes/posts&lt;/code&gt;. The simplest solution is to create a symbolic link.&lt;/p&gt;
          &lt;p&gt;First, open Terminal and make sure you’re in the root of your monorepo (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/personal-monorepo&lt;/code&gt;). &lt;strong&gt;Back up your content&lt;/strong&gt;, then delete the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory:&lt;/p&gt;
          &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; website/_posts
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Next, create a symlink that points to your Obsidian posts:&lt;/p&gt;
          &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;website
          &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; ../notes/_posts _posts
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Folder names in symlinks are case-sensitive. If your Obsidian vault uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Posts&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt;, your symlink won’t work with the above command. Folder names must match exactly.&lt;/p&gt;
          &lt;h2 id=&quot;publishing-with-netlify&quot;&gt;Publishing with Netlify&lt;/h2&gt;
          &lt;p&gt;&lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; provides first-class support for working with monorepos, but you’ll need to do a little additional configuration.&lt;/p&gt;
          &lt;p&gt;In your project dashboard or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify.toml&lt;/code&gt; file, set the base directory to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website&lt;/code&gt;, and make sure the build command installs dependencies before running Jekyll:&lt;/p&gt;
          &lt;p&gt;Here’s what my full &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify.toml&lt;/code&gt; looks like, and what is reflected in my dashboard:&lt;/p&gt;
          &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;base = &quot;website&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;command = &quot;bundle install &amp;amp;&amp;amp; bundle exec jekyll build&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;publish = &quot;_site&quot;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Netlify follows symlinks automatically, but Jekyll needs one extra setting. In your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website/_config.yml&lt;/code&gt;, add:&lt;/p&gt;
          &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Enable access to symlinked content&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;lax_symlink_lookup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;This lets Jekyll access files stored outside its root directory (e.g. the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;website&lt;/code&gt; folder). Without the site will build but won’t pull in your content from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notes/_posts&lt;/code&gt; directory.&lt;/p&gt;
          &lt;h2 id=&quot;final-workflow&quot;&gt;Final Workflow&lt;/h2&gt;
          &lt;p&gt;I have Obsidian configured to add new notes to my writing inbox under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~inbox&lt;/code&gt; directory. Anything I start writing goes there. Once I’ve finished writing a note, I can then either move it into my permanent notes or add it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt; directory if I want to publish it. Once I push the commit Netlify will build and publish my site.&lt;/p&gt;
          &lt;p&gt;While I’m writing, I can preview what the note will look like by starting the Jekyll development server (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec jekyll serve&lt;/code&gt;) and adding the note to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt; directory. I can use the Obsidian hotlinks command to open a preview of the post in my browser.&lt;/p&gt;
          &lt;p&gt;To wrap up, your final workflow looks something this this:&lt;/p&gt;
          &lt;ol&gt;
          &lt;li&gt;Write, edit, and link notes in Obsidian&lt;/li&gt;
          &lt;li&gt;Commit and push changes to Github&lt;/li&gt;
          &lt;li&gt;Netlify will pick up the changes and publish your posts&lt;/li&gt;
          &lt;/ol&gt;
          &lt;h2 id=&quot;bonus-convert-backlinks-to-weblinks&quot;&gt;Bonus: Convert Backlinks to Weblinks&lt;/h2&gt;
          &lt;p&gt;It’s outside the scope of this post, but I also wrote a custom plugin to convert backlinks to web links. That way I can use Obsidian’s backlinks within my published posts.&lt;/p&gt;
          &lt;p&gt;&lt;a href=&quot;https://github.com/andrewstiefel/andrewstiefel.com/blob/main/_plugins/backlinks.rb&quot;&gt;You can grab the code here&lt;/a&gt;.&lt;/p&gt;
          &lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h2&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://alexoliveira.cc/guide/jekyll-with-obsidian&quot;&gt;Jekyll Blogging with Obsidian&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://refinedmind.co/obsidian-jekyll-workflow&quot;&gt;Obsidian Jekyll workflow&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://dev.to/adrianogil/blogging-with-obsidian-and-jekyll-5bgl&quot;&gt;Blogging with Obsidian and Jekyll&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://stephango.com/vault&quot;&gt;How I Use Obsidian&lt;/a&gt;&lt;/li&gt;
          &lt;/ul&gt;
        </content>
      </entry>
      <entry>
        <title>How I Use GitHub as a CMS</title>
        <link rel="alternate" href="https://andrewstiefel.com/github-cms-blog/"/>
        <published>2025-02-02T00:00:00-08:00</published>
        <id>https://andrewstiefel.com/github-cms-blog</id>
        <summary>GitHub makes it easy to build a free content management system for your blog.</summary>
        <content type="html">&lt;p&gt;I use &lt;a href=&quot;/blog-jekyll-netlify/&quot; class=&quot;internal-link&quot; data-preview-title=&quot;How I Built My Blog with Jekyll and Netlify&quot; data-preview-excerpt=&quot;I’ve been blogging and hosting my website since 2006, but I’ve always been unhappy with the themes available for technologies like Blogger, WordPress, or Squarespace. I usually had a vision for what I wanted to create and would spend hours scouring marketplaces to find something that came close.&quot;&gt;Jekyll to build and publish my blog on Netlify&lt;/a&gt;. For a long time my content management system (CMS) was just a bunch of markdown files on my laptop. Most of the time I don’t need anything else — it’s simple, and it’s just me.&lt;/p&gt;
          &lt;p&gt;But as I’ve wanted to focus on writing more often, I wanted to find an easier way to track future ideas, what I’d like to work on now, and easily see what I’ve done.&lt;/p&gt;
          &lt;p&gt;That’s where GitHub comes in.&lt;/p&gt;
          &lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;
          &lt;p&gt;GitHub is built for software development, but many of the features work just as well for content development. In fact, is has about everything I need as you’ll see in my setup section below. This includes templates, project boards, and workflows for publishing.&lt;/p&gt;
          &lt;p&gt;Plus it’s easy to review and collaborate with other people. I don’t do that much as a solo writer, but I do get readers who spot typos or who share feedback—and then it’s easy to track and add their contributions.&lt;/p&gt;
          &lt;p&gt;But let’s be honest — this is one of the projects I took on because I could, not so much because it really saves me any time. Choose your adventure accordingly :)&lt;/p&gt;
          &lt;h2 id=&quot;basic-setup&quot;&gt;Basic setup&lt;/h2&gt;
          &lt;p&gt;If you’re already using GitHub to host the code for your blog (like I do with Jekyll), then you can get started with this in 15 minutes or less!&lt;/p&gt;
          &lt;ol&gt;
          &lt;li&gt;&lt;strong&gt;Create a project:&lt;/strong&gt; Go to the GitHub repository for your blog (or make a new one!) and select projects and &lt;a href=&quot;https://docs.github.com/en/issues/planning-and-tracking-with-projects/creating-projects/creating-a-project&quot;&gt;create your project&lt;/a&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Choose a format:&lt;/strong&gt; GitHub projects lets you &lt;a href=&quot;https://docs.github.com/en/issues/planning-and-tracking-with-projects/customizing-views-in-your-project&quot;&gt;customize views&lt;/a&gt; of items in your project using a Kanban board or table format. I recommend starting with the Kanban format. You can always change this later.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Organize your workflow:&lt;/strong&gt; Once you’ve created your project, take a couple minutes to configure your workflow. I have sections for no status (my backlog), in progress, ready, and done.&lt;/li&gt;
          &lt;/ol&gt;
          &lt;p&gt;You can &lt;a href=&quot;https://github.com/users/andrewstiefel/projects/2&quot;&gt;see an example of mine on GitHub&lt;/a&gt;, or with the screenshot below:&lt;/p&gt;
          &lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_725/https://andrewstiefel.com/assets/img/github-cms.png&quot; srcset=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_320/https://andrewstiefel.com/assets/img/github-cms.png 320w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_602/https://andrewstiefel.com/assets/img/github-cms.png 602w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_884/https://andrewstiefel.com/assets/img/github-cms.png 884w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1166/https://andrewstiefel.com/assets/img/github-cms.png 1166w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1448/https://andrewstiefel.com/assets/img/github-cms.png 1448w&quot; sizes=&quot;(min-width: 50rem) 50rem, 90vw&quot; data-lightbox=&quot;&quot; data-full=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/q_auto,f_auto/https://andrewstiefel.com/assets/img/github-cms.png&quot; alt=&quot;GitHub CMS&quot; width=&quot;2990&quot; height=&quot;1712&quot; crossorigin=&quot;anonymous&quot; class=&quot;dark:brightness-75 cursor-pointer&quot; /&gt;&lt;/p&gt;
          &lt;h2 id=&quot;use-issues-to-track-posts&quot;&gt;Use issues to track posts&lt;/h2&gt;
          &lt;p&gt;Once you’ve created the basic structure, it’s time to start tracking your writing! You’ll want to create new issues so you can track your progress. I decided to create a new template so I wouldn’t have to add the same info every time.&lt;/p&gt;
          &lt;p&gt;If you’d like to create one, all you need to add is a markdown file in your repository at the location below:&lt;/p&gt;
          &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
          ├── .github 
          │   └── ISSUE_TEMPLATE
          │      └── cms.md
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Here’s &lt;a href=&quot;https://github.com/andrewstiefel/andrewstiefel.com/blob/main/.github/ISSUE_TEMPLATE/cms.md?plain=1&quot;&gt;an example&lt;/a&gt; of what mine looks like:&lt;/p&gt;
          &lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cms&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;about&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Submit an idea for the blog&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[blog&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;post]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cms&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;assignees&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;
          &lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
          &lt;span class=&quot;gs&quot;&gt;**What is this post about?**&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;gs&quot;&gt;**Outline**&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;1.&lt;/span&gt; 
          &lt;span class=&quot;gs&quot;&gt;**Tasks**&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; [ ] Write outline
          &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; [ ] Draft blog post
          &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; [ ] Find (1) reviewer
          &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; [ ] Create pull request
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;I use the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[blog post] example title&lt;/code&gt; so I can see at a glance that the issue covers a new blog post, and roughly what it’s about. I provide a brief description (usually when I first have the idea). Later I’ll come back and outline the post.&lt;/p&gt;
          &lt;h2 id=&quot;writing-and-publishing-posts&quot;&gt;Writing and publishing posts&lt;/h2&gt;
          &lt;p&gt;Jekyll comes with basic structure which makes it easy to get started. I save my draft writing in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_drafts&lt;/code&gt; folder and move finished posts into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; folder.&lt;/p&gt;
          &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
          ├── _drafts 
          ├── _posts
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;But you have a few options to customize your workflow a bit further:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Work directly in your main branch&lt;/strong&gt; and publish by moving the your finished drafts from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_drafts&lt;/code&gt; folder to the  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; folder.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Create a new branch&lt;/strong&gt; for each post as you work and merge into your main branch when you’re ready to publish.&lt;/li&gt;
          &lt;/ul&gt;
          &lt;blockquote&gt;
          &lt;p&gt;&lt;strong&gt;JEKYLL TIP&lt;/strong&gt; 
          You can preview your posts in your development environment as you work! Just append the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt;drafts flag to the build or serve command. For example,  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll serve --drafts&lt;/code&gt;. Each draft post will be added using the last modified time as the publication date.&lt;/p&gt;
          &lt;/blockquote&gt;
          &lt;p&gt;I prefer to create a new branch and to submit a pull request when I’m ready to publish (although I’ll admit, I’m not super consistent since it’s just me). Either way, when you’re ready to publish, you’ll &lt;a href=&quot;https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue&quot;&gt;close the issue&lt;/a&gt; for your blog by writing a commit message that includes the number of the issue. For example, when I published this blog, I used “Closes 212” to tell GitHub to mark the issue as done.&lt;/p&gt;
          &lt;p&gt;Now if you visit your project, you’ll see your post has automatically been moved to the “done” column!&lt;/p&gt;
          &lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_725/https://andrewstiefel.com/assets/img/github-pull-request.png&quot; srcset=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_320/https://andrewstiefel.com/assets/img/github-pull-request.png 320w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_602/https://andrewstiefel.com/assets/img/github-pull-request.png 602w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_884/https://andrewstiefel.com/assets/img/github-pull-request.png 884w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1166/https://andrewstiefel.com/assets/img/github-pull-request.png 1166w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1166/https://andrewstiefel.com/assets/img/github-pull-request.png 1166w&quot; sizes=&quot;(min-width: 50rem) 50rem, 90vw&quot; data-lightbox=&quot;&quot; data-full=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/q_auto,f_auto/https://andrewstiefel.com/assets/img/github-pull-request.png&quot; alt=&quot;GitHub Pull Request&quot; width=&quot;1166&quot; height=&quot;474&quot; crossorigin=&quot;anonymous&quot; class=&quot;dark:brightness-75 cursor-pointer&quot; /&gt;&lt;/p&gt;
          &lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;
          &lt;p&gt;I’ve been exploring ways to &lt;a href=&quot;https://docs.github.com/en/issues/planning-and-tracking-with-projects/customizing-views-in-your-project&quot;&gt;customize this further by creating new views&lt;/a&gt; — for example, a table view that adds publication dates so I can when I want to publish a post. But most importantly, I’m enjoying the flexibility and close integration between what I write and how it’s published.&lt;/p&gt;
          &lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;/blog-jekyll-netlify/&quot; class=&quot;internal-link&quot; data-preview-title=&quot;How I Built My Blog with Jekyll and Netlify&quot; data-preview-excerpt=&quot;I’ve been blogging and hosting my website since 2006, but I’ve always been unhappy with the themes available for technologies like Blogger, WordPress, or Squarespace. I usually had a vision for what I wanted to create and would spend hours scouring marketplaces to find something that came close.&quot;&gt;How I Built My Blog with Jekyll and Netlify&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;/markdown-files-not-apps/&quot; class=&quot;internal-link&quot; data-preview-title=&quot;Markdown Files, Not Apps&quot; data-preview-excerpt=&quot;Files, not apps. I&amp;#39;m convinced this is the best way to work in the future.\n\nI write down almost everything important in my life: lists, ideas, plans, code, and articles. They form my extended memory. They are a record of what I&amp;#39;ve done, and who I&amp;#39;ve been.&quot;&gt;Markdown Files, Not Apps&lt;/a&gt;&lt;/li&gt;
          &lt;/ul&gt;
        </content>
      </entry>
      <entry>
        <title>Working with Multiple GitHub Accounts and SSH Keys</title>
        <link rel="alternate" href="https://andrewstiefel.com/working-multiple-github-accounts/"/>
        <published>2024-05-09T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/working-multiple-github-accounts</id>
        <summary>Learn how to use 1Password to sign Git commits for multiple GitHub accounts on one machine</summary>
        <content type="html">&lt;p&gt;I have multiple GitHub accounts – one for work, one for demos, and one for personal projects. Each has a unique email address, password, and 2FA associated with it. They also each have a unique SSH key. In fact, the SSH keys are all saved in different 1Password accounts (personal, demo, and work).&lt;/p&gt;
          &lt;p&gt;In some cases I perform the development on the same device, like when I’m building a demo on my work device. In that case I need to make sure that I’m using the correct GitHub account and SSH key.&lt;/p&gt;
          &lt;p&gt;I use 1Password as my &lt;a href=&quot;https://developer.1password.com/docs/ssh&quot;&gt;SSH agent&lt;/a&gt; because it keeps the private keys off my device, plus it makes authorizing SSH connections a breeze. I can authenticate a connection using Touch ID, so just a fingerprint touch on my keyboard and then I’m off to my next task.&lt;/p&gt;
          &lt;p&gt;Fortunately, this only takes a few minutes to setup!&lt;/p&gt;
          &lt;h2 id=&quot;organize-your-local-directory&quot;&gt;Organize your local directory&lt;/h2&gt;
          &lt;p&gt;All my repositories are saved under a GitHub folder and then I use directories to organize my projects by account. Although I prefer to keep personal and work separate, you could configure all three accounts for a single device like in the example below:&lt;/p&gt;
          &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/github
          ├── /demo
          ├── /personal
          └── /work
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;This will make it very easy to configure which profile and SSH key to use in the following steps.&lt;/p&gt;
          &lt;h2 id=&quot;set-your-global-gitconfig-file&quot;&gt;Set your global gitconfig file&lt;/h2&gt;
          &lt;p&gt;In this example, I’m setting my personal Git configuration as the global default. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[includeIf]&lt;/code&gt; I specify a different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitconfig&lt;/code&gt; file for the &lt;strong&gt;demo &lt;/strong&gt;and &lt;strong&gt;work &lt;/strong&gt;directories.&lt;/p&gt;
          &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;user]
          name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;github_personal_name&amp;gt;
          email &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;github_personal_email&amp;gt;
          signingkey &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;your_ssh_key&amp;gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;gpg]
          format &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ssh
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;gpg &lt;span class=&quot;s2&quot;&gt;&quot;ssh&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
          program &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/Applications/1Password.app/Contents/MacOS/op-ssh-sign&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;commit]
          gpgsign &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;includeIf &lt;span class=&quot;s2&quot;&gt;&quot;gitdir:~/github/demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
          path &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ~/github/demo/.gitconfig
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;includeIf &lt;span class=&quot;s2&quot;&gt;&quot;gitdir:~/github/work&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
          path &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ~/github/work/.gitconfig
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;create-gitconfig-files-for-each-directory&quot;&gt;Create gitconfig files for each directory&lt;/h2&gt;
          &lt;p&gt;Next I’ll create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitconfig&lt;/code&gt; files and save them in the &lt;strong&gt;demo &lt;/strong&gt;and &lt;strong&gt;work &lt;/strong&gt;directories. I’ll use a similar template, but this time I’ll provide the GitHub name, email, and public signing key for my demo and work accounts.&lt;/p&gt;
          &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;user]
          name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;github_work_name&amp;gt;
          email &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;github_work_email&amp;gt;
          signingkey &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &amp;lt;your_ssh_key&amp;gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;gpg]
          format &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ssh
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;gpg &lt;span class=&quot;s2&quot;&gt;&quot;ssh&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
          program &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/Applications/1Password.app/Contents/MacOS/op-ssh-sign&quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;commit]
          gpgsign &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;configure-the-ssh-agent&quot;&gt;Configure the SSH agent&lt;/h2&gt;
          &lt;p&gt;In order to call the correct SSH keys, I’ll need to update the SSH agent config file located at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;I start by setting the 1Password SSH agent as the default for all hosts. Then I create custom hosts for each account, in this case&lt;strong&gt; Demo&lt;/strong&gt;,&lt;strong&gt; Personal&lt;/strong&gt;, and&lt;strong&gt; Work&lt;/strong&gt;.&lt;/p&gt;
          &lt;p&gt;Next, I downloaded the public keys from 1Password for my demo, personal, and work accounts. I renamed each file (for example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demo_git.pub&lt;/code&gt;) and saved the all to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/&lt;/code&gt; folder.&lt;/p&gt;
          &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# By default, use the 1Password SSH agent for all hosts&lt;/span&gt;
          Host &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
          IdentityAgent &lt;span class=&quot;s2&quot;&gt;&quot;~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock&quot;&lt;/span&gt;
          &lt;span class=&quot;c&quot;&gt;# Demo GitHub&lt;/span&gt;
          Host gh-demo
          HostName github.com
          User git
          IdentityFile ~/.ssh/demo_git.pub
          IdentitiesOnly &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
          &lt;span class=&quot;c&quot;&gt;# Personal GitHub&lt;/span&gt;
          Host gh-personal
          HostName github.com
          User git
          IdentityFile ~/.ssh/personal_git.pub
          IdentitiesOnly &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
          &lt;span class=&quot;c&quot;&gt;# Work GitHub&lt;/span&gt;
          Host gh-work
          HostName github.com
          User git
          IdentityFile ~/.ssh/work_git.pub
          IdentitiesOnly &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;reset-your-local-repositories&quot;&gt;Reset your local repositories&lt;/h2&gt;
          &lt;p&gt;Finally, I’ll need to reset each individual repository so it uses one of the hosts specified above. This will make sure it uses the correct SSH key to authenticate with GitHub and when pushing Git commits.&lt;/p&gt;
          &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git remote set-url origin &amp;lt;host&amp;gt;:&amp;lt;organization&amp;gt;/&amp;lt;repository&amp;gt;.git
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Finally, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git remote -v&lt;/code&gt; to confirm that Git is using the correct remote repository. You can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt; to confirm that 1Password offers up the correct SSH key for authentication.&lt;/p&gt;
          &lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://developer.1password.com/docs/ssh/git-commit-signing#configure-multiple-commit-signing-setups&quot; title=&quot;Configure Multiple Git Commit Signing Setups&quot;&gt;Configure Multiple Git Signing Setups&lt;/a&gt; (1Password)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://developer.1password.com/docs/ssh/agent/advanced#use-multiple-github-accounts&quot; title=&quot;Use Multiple GitHub Accounts&quot;&gt;Use Multiple GitHub Accounts&lt;/a&gt; (1Password)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account&quot; title=&quot;Adding a New SSH Key to Your GitHub Account&quot;&gt;Adding a New SSH Key to Your GitHub Account&lt;/a&gt; (GitHub)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://developer.1password.com/docs/ssh/git-commit-signing#step-2-register-your-public-key&quot; title=&quot;Register a Public SSH Key with GitHub&quot;&gt;Register a Public SSH Key with GitHub&lt;/a&gt; (1Password)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection&quot; title=&quot;Testing Your SSH Connection&quot;&gt;Testing Your SSH Connection&lt;/a&gt; (GitHub)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/authentication/troubleshooting-ssh/error-permission-denied-publickey#verify-the-public-key-is-attached-to-your-account&quot; title=&quot;Switching Remote URLs from HTTPS to SSH&quot;&gt;Switching Remote URLs from HTTPS to SSH&lt;/a&gt; (GitHub)&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://dev.to/sisco/optimize-your-git-setup-strategies-for-handling-multiple-github-accounts-3ji8&quot; title=&quot;Optimize Your Git Setup&quot;&gt;Optimize Your Git Setup: Strategies for Handling Multiple GitHub Accounts&lt;/a&gt; (Dev.to)&lt;/li&gt;
          &lt;/ul&gt;
        </content>
      </entry>
      <entry>
        <title>Pocket Guide to Privacy-First Email Newsletter Software</title>
        <link rel="alternate" href="https://andrewstiefel.com/privacy-first-email-newsletter-software/"/>
        <published>2023-02-09T00:00:00-08:00</published>
        <id>https://andrewstiefel.com/privacy-first-email-newsletter-software</id>
        <summary>A handy guide to privacy-first email newsletter services that protect user privacy by turning off open and click tracking by default.</summary>
        <content type="html">&lt;p&gt;When I first started to look for privacy-first email newsletter options back in 2019, there just weren’t many options. There were great privacy-first website analytics tools like &lt;a href=&quot;https://usefathom.com/ref/FBJDFZ&quot; title=&quot;Fathom Analytics&quot; data-fathom=&quot;Referral click&quot; target=&quot;_blank&quot;&gt;Fathom Analytics&lt;/a&gt;, but nothing for email. Nearly every provider required tracking pixels by default.&lt;/p&gt;
          &lt;p&gt;I want to briefly acknowledge that there are some legitimate reasons why email newsletter software providers might require pixels. Tracking pixels are one way to evaluate a sender’s reputation and ensure overall deliverability for everyone using their platform. Higher open and click rates indicate that a list is engaged. The reverse could indicate that an account is sending spam emails.&lt;/p&gt;
          &lt;p&gt;The problem is that tracking pixels collect far too much information, including exact location information from each recipient. Most of us understandably don’t want senders to know where we read an email, how many times we read that email, what links we clicked, and how many times we clicked each link.&lt;/p&gt;
          &lt;p&gt;There are, of course, other ways to evaluate list engagement. Unsubscribes and spam reports are a clear signal – and they are the indicators that matter most. As a sender, you can evaluate email engagement by website traffic, list growth, unsubscribes, spam reports, and other methods.&lt;/p&gt;
          &lt;p&gt;The good news is that over the past year or two many major email newsletter providers have started to let you turn off email trackers (e.g. MailChimp, but with limitations). Other providers still do not (e.g. ConvertKit).&lt;/p&gt;
          &lt;h2 id=&quot;privacy-first-email-newsletter-services&quot;&gt;Privacy-First Email Newsletter Services&lt;/h2&gt;
          &lt;p&gt;I wanted a provider that offered a privacy-first by default platform. So I compiled a list and I’m sharing it here. My criteria for inclusion in this list were simple:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;Open and click tracking are turned off by default&lt;/li&gt;
          &lt;li&gt;Does not use Google ReCaptcha on the subscription form&lt;/li&gt;
          &lt;li&gt;Does not have third-party trackers (e.g. Google Analytics) on their website&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;Below is a list of the email newsletter services that met the criteria, as well as their lowest price point as of February 9, 2023:&lt;/p&gt;
          &lt;table&gt;
          &lt;thead&gt;
          &lt;tr&gt;
          &lt;th&gt;Company&lt;/th&gt;
          &lt;th&gt;Tracking&lt;/th&gt;
          &lt;th&gt;HQ Location&lt;/th&gt;
          &lt;th&gt;Cheapest Plan&lt;/th&gt;
          &lt;/tr&gt;
          &lt;/thead&gt;
          &lt;tbody&gt;
          &lt;tr&gt;
          &lt;td&gt;Buttondown&lt;/td&gt;
          &lt;td&gt;Off by default&lt;/td&gt;
          &lt;td&gt;US&lt;/td&gt;
          &lt;td&gt;Free up to 100 subscribers&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
          &lt;td&gt;SendStack&lt;/td&gt;
          &lt;td&gt;None&lt;/td&gt;
          &lt;td&gt;EU&lt;/td&gt;
          &lt;td&gt;Free up to 100 subscribers&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
          &lt;td&gt;MailCoach&lt;/td&gt;
          &lt;td&gt;Off by default&lt;/td&gt;
          &lt;td&gt;EU&lt;/td&gt;
          &lt;td&gt;$9.99&lt;/td&gt;
          &lt;/tr&gt;
          &lt;/tbody&gt;
          &lt;/table&gt;
          &lt;h2 id=&quot;buttondown&quot;&gt;Buttondown&lt;/h2&gt;
          &lt;p&gt;&lt;a href=&quot;https://buttondown.email/refer/andrewstiefel&quot; title=&quot;Buttondowm&quot; target=&quot;_blank&quot; data-fathom=&quot;Referral click&quot;&gt;Buttondown&lt;/a&gt; is a minimalist tool for writing and producing email newsletters. Based in the United States, it is run as an indie software business. Buttondown gets a lot of things right, like privacy-first by default (you can, however, opt-in to tracking if you want), the ability to write in markdown, RSS-to-Email tools so you can run your own version of Substack, and a first-party API that is included in the free plan for the hackers among us.&lt;/p&gt;
          &lt;h2 id=&quot;sendstack&quot;&gt;SendStack&lt;/h2&gt;
          &lt;p&gt;&lt;a href=&quot;https://getsendstack.com/&quot; title=&quot;SendStack&quot; target=&quot;_blank&quot;&gt;SendStack&lt;/a&gt; is one of the newest tools and was released by a team based in the European Union (Germany, specifically). It has fewer features than Buttondown but a much friendlier user interface. They also make their first-party API available to free plans, which is a great feature for developers or anyone wanting to &lt;a href=&quot;https://andrewstiefel.com/netlify-functions-email-subscription/&quot;&gt;build their own first-party email newsletter subscription form&lt;/a&gt;. SendStack takes a much harder line on automation, however, so there is no RSS-to-Email function or other automation tools.&lt;/p&gt;
          &lt;h2 id=&quot;mailcoach&quot;&gt;MailCoach&lt;/h2&gt;
          &lt;p&gt;&lt;a href=&quot;https://mailcoach.app/&quot; title=&quot;MailCoach&quot; target=&quot;_blank&quot;&gt;MailCoach&lt;/a&gt; offers both a hosted and a cloud-based solution. Interestingly, they also support transactional email. Like the other platforms here they offer a streamlined interface. But unlike Buttondown and SendStack, automation and transactional email are treated as important features. This is a good solution if you need more than just a newsletter, and would be a great option for agencies, businesses, or anyone building more complex email workflows.&lt;/p&gt;
          &lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
          &lt;p&gt;I have personally used Buttondown and SendStack, but I haven’t tried MailCoach yet. This isn’t an endorsement of any particular service. You should try them out and see what works best for you. I also recommend this article comparing &lt;a href=&quot;https://blog.daniemon.com/2022/11/15/privacy-first-newsletters-transactional-emails/&quot; title=&quot;Pricing Comparison&quot; target=&quot;_blank&quot;&gt;pricing models for privacy-first email newsletter services&lt;/a&gt; if you are considering one of these platforms.&lt;/p&gt;
          &lt;p&gt;But, if you’ve been looking for a private alternative to Mailchimp and similar services, you now have a handful to choose from. I’m looking forward to seeing this space grow as more creators and businesses start to adopt privacy-first marketing practices in the future.&lt;/p&gt;
        </content>
      </entry>
      <entry>
        <title>Build an Email Subscription Form with Netlify Functions</title>
        <link rel="alternate" href="https://andrewstiefel.com/netlify-functions-email-subscription/"/>
        <published>2022-08-28T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/netlify-functions-email-subscription</id>
        <summary>Learn how to use Netlify Functions and ConvertKit to create a custom newsletter subscription form for your website.</summary>
        <content type="html">&lt;p&gt;I enjoy the process of building and maintaining my own personal website. It’s a great way to experiment with different technologies, and have fun learning new tools and concepts along the way.&lt;/p&gt;
          &lt;p&gt;This time, I wanted to learn how to use &lt;a href=&quot;https://www.netlify.com/blog/intro-to-serverless-functions/&quot; title=&quot;Intro to Serverless Functions – Netlify&quot;&gt;serverless functions&lt;/a&gt;. There are a lot of great resources out there already. But I had trouble finding a guide that adequately addressed my use case: a humble email subscription form.&lt;/p&gt;
          &lt;p&gt;This tutorial is strongly inspired by an &lt;a href=&quot;https://css-tricks.com/using-netlify-forms-and-netlify-functions-to-build-an-email-sign-up-widget/&quot; title=&quot;CSS-Tricks&quot;&gt;excellent guide created by Matthew Ström&lt;/a&gt;. I’ve added solutions for some of the problems I encountered while following his guide, but Matthew deserves the credit.&lt;/p&gt;
          &lt;h2 id=&quot;the-challenge-build-a-mailing-list-sign-up-form&quot;&gt;The Challenge: Build a Mailing List Sign Up Form&lt;/h2&gt;
          &lt;p&gt;After going through all the work to build my personal website, the last thing I wanted to do was slap a pre-designed newsletter subscription form on it.&lt;/p&gt;
          &lt;p&gt;I wanted the flexibility to design my own custom forms for a few reasons:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Better design:&lt;/strong&gt; Email marketing providers offer great default forms, but they never perfectly match a site’s design&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Better Performance:&lt;/strong&gt; External email forms require additional calls for CSS and Javascript, and they sometimes get blocked by privacy settings&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Better privacy:&lt;/strong&gt; Hosted email forms can collect additional information about users, like their IP address&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;I set out a few rules for this challenge:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;It should work without extra JavaScript or AJAX&lt;/li&gt;
          &lt;li&gt;It must use &lt;a href=&quot;https://docs.netlify.com/functions/overview/&quot; title=&quot;Netlify Functions Overview&quot;&gt;Netlify functions&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;It shouldn’t need external dependencies&lt;/li&gt;
          &lt;/ul&gt;
          &lt;h2 id=&quot;the-team-jekyll-netlify-and-convertkit&quot;&gt;The Team: Jekyll, Netlify, and ConvertKit&lt;/h2&gt;
          &lt;p&gt;My website is built using a static site generator called &lt;a href=&quot;https://jekyllrb.com/&quot; title=&quot;Jekyll&quot;&gt;Jekyll&lt;/a&gt;. It allows me to build my own templates and components, so I’ll use it to build my email form. I also used &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-forms&quot; title=&quot;Github – Tailwind CSS Forms&quot;&gt;Tailwind CSS Forms Plugin&lt;/a&gt; to simplify the design process.&lt;/p&gt;
          &lt;p&gt;I use a service called &lt;a href=&quot;https://www.netlify.com/&quot; title=&quot;Netlify&quot;&gt;Netlify&lt;/a&gt; to deploy my website. It’s key for this project, because it complies the static assets built by Jekyll and runs the serverless function to send emails to my email list provider.&lt;/p&gt;
          &lt;p&gt;Finally, I’m using &lt;a href=&quot;https://convertkit.com/&quot; title=&quot;ConvertKit&quot;&gt;ConvertKit&lt;/a&gt; as my email list provider. I’ve also included information on how to use &lt;a href=&quot;https://buttondown.email&quot; title=&quot;Buttondown&quot;&gt;Buttondown&lt;/a&gt; and &lt;a href=&quot;https://getsendstack.com/&quot; title=&quot;SendStack&quot;&gt;SendStack&lt;/a&gt; in this tutorial, but the basic principles of the function should apply to any other email providers that offers an API.&lt;/p&gt;
          &lt;p&gt;Ok, let’s get started!&lt;/p&gt;
          &lt;h2 id=&quot;create-the-serverless-function&quot;&gt;Create the serverless function&lt;/h2&gt;
          &lt;p&gt;You need to follow three basic steps to create a Netlify function:&lt;/p&gt;
          &lt;ol&gt;
          &lt;li&gt;Add API tokens to Netlify as environment variables via the admin interface&lt;/li&gt;
          &lt;li&gt;Tells Netlify where to look for your functions using the netlify.toml file 3. Write the function in a Javascript file in your project&lt;/li&gt;
          &lt;li&gt;Write the function as a Javascript file in your project&lt;/li&gt;
          &lt;/ol&gt;
          &lt;p&gt;To start, let’s save the API token from our email service as an &lt;strong&gt;environment variable&lt;/strong&gt;. Environment variables are useful to hold information that I don’t want to make public, like this API key. You can add an environment variable using the Netlify admin interface under your build and deploy settings.&lt;/p&gt;
          &lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_725/https://andrewstiefel.com/assets/img/netlify-environment-variables.png&quot; srcset=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_320/https://andrewstiefel.com/assets/img/netlify-environment-variables.png 320w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_602/https://andrewstiefel.com/assets/img/netlify-environment-variables.png 602w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_884/https://andrewstiefel.com/assets/img/netlify-environment-variables.png 884w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1166/https://andrewstiefel.com/assets/img/netlify-environment-variables.png 1166w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_1448/https://andrewstiefel.com/assets/img/netlify-environment-variables.png 1448w&quot; sizes=&quot;(min-width: 50rem) 50rem, 90vw&quot; data-lightbox=&quot;&quot; data-full=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/q_auto,f_auto/https://andrewstiefel.com/assets/img/netlify-environment-variables.png&quot; alt=&quot;Netlify environment variables&quot; width=&quot;1880&quot; height=&quot;782&quot; crossorigin=&quot;anonymous&quot; class=&quot;dark:brightness-75 cursor-pointer&quot; /&gt;&lt;/p&gt;
          &lt;p&gt;Next, specify where Netlify should look for your functions. Edit your netlify.toml to specify the functions directory. It might look something like this:&lt;/p&gt;
          &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;base = &quot;.&quot;&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;directory = &quot;netlify/functions/&quot;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Create a function file in the directory you specified above. If you used the default functions directory, you should save your function at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YOUR_BASE_DIRECTORY/netlify/functions&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;Next, you’ll need to give your function a name. For example, to create a function with an endpoint name of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt;, save the function in one of these locations:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify/functions/hello-world.js&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify/functions/hello-world/hello-world.js&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify/functions/hello-world/index.js&lt;/code&gt;&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;For this tutorial, I’m going to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;submission-created&lt;/code&gt; event trigger. Netlify will run my function every time a form is submitted. To do that, I’m going to name my function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;submission-created.js&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;Now you’re ready to start writing the function. Start by importing the API key you created earlier as an environment variable:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;On line 2, I add a small library called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node-fetch&lt;/code&gt;. This allows me to use Javascript’s Fetch API, which is how we’ll format an API POST request to send data to our email service.&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;node-fetch&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;NOTE: When I was writing this post, many of the tutorials available used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require&lt;/code&gt; method to import the Fetch API which resulted in errors when I tried to deploy the function. Make sure you use the method I described above. If you upgrade to node-fetch v3, you’ll also need to update either your netlify.toml file or package.json to use ESM.&lt;/p&gt;
          &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;node_bundler = &quot;esbuild&quot;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;You can find more information about &lt;a href=&quot;https://www.netlify.com/blog/how-to-make-a-fetch-request-using-node-fetch-v3/&quot; title=&quot;Netlify Blog&quot;&gt;how to make a fetch request using node-fetch v3&lt;/a&gt; in an excellent guide by Tatyana Novell on the Netlify blog.&lt;/p&gt;
          &lt;p&gt;Next create a synchronous function on line 4. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exports.handler&lt;/code&gt; value is where Netlify expects to find the function, so I define it there. The basic syntax to create the function is provided below:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;function &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// your server-side functionality&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Next retrieve the email from the event value using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt;:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Then log the data in the console for debugging:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Received a submission: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;After retrieving the email address from the event value using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt;, I’m ready to send it off my email marketing providers. I’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node-fetch&lt;/code&gt; library I imported earlier to form the POST request.&lt;/p&gt;
          &lt;p&gt;I’ve outlined code examples for a few services below, but make sure you consult the API documentation from your email provider to make sure the API request is properly format.&lt;/p&gt;
          &lt;h3 id=&quot;convertkit-subscription-form&quot;&gt;ConvertKit Subscription Form&lt;/h3&gt;
          &lt;p&gt;Using ConvertKit as an example, send a POST request to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://api.convertkit.com/v3/forms/&amp;lt;YOUR_FORM_ID/subscribe&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;To find the form ID, navigate to &lt;strong&gt;Grow &amp;gt; Landing Pages &amp;amp; Forms&lt;/strong&gt;, select the form you want to use, and copy the ID number from the url:&lt;/p&gt;
          &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://app.convertkit.com/forms/designers/&lt;span class=&quot;nt&quot;&gt;&amp;lt;YOUR_FORM_ID&amp;gt;&lt;/span&gt;/edit
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;The body of the POST request contains the email token and the email address from the form submission:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.convertkit.com/v3/forms/&amp;lt;YOUR_FORM_ID/subscribe&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; 
          &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h3 id=&quot;buttondown-subscription-form&quot;&gt;Buttondown Subscription Form&lt;/h3&gt;
          &lt;p&gt;&lt;a href=&quot;https://buttondown.email/refer/andrewstiefel&quot; data-fathom=&quot;Referral click&quot;&gt;Buttondown’s API&lt;/a&gt; sends the authorization in the headers rather than the body, so you’ll need to adapt the code slightly:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.buttondown.email/v1/subscribers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`Token &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h3 id=&quot;sendstack-subscription-form&quot;&gt;SendStack Subscription Form&lt;/h3&gt;
          &lt;p&gt;SendStack is a new privacy-first email service. Unlike Buttondown and ConvertKit, they offer API access on their free plan. There is currently a waiting list, but if you have access, you can try it out using the code below.&lt;/p&gt;
          &lt;p&gt;Add the email token to the headers and the email address to the body of the POST request:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://getsendstack.com/api/subscribers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`Bearer &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Then capture and log the response from the email service. We do this to diagnose any issues that happened. Netlify makes it easy to check your function’s logs, so use console.log often!&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;responseText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Response:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;responseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Finally, redirect the form to a confirmation page that tells subscribers to check their emails to confirm their subscription. Use a simple return to redirect the browser to the new page:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;302&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/confirmation/,
          },
          }
          &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;You can find the completed function below. In this case I used ConvertKit:&lt;/p&gt;
          &lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;node-fetch&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Received a submission: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.convertkit.com/v3/forms/{YOUR_FORM-ID}/subscribe&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; 
          &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EMAIL_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;responseText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;response:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;responseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;302&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/confirmation/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;create-the-email-subscription-form&quot;&gt;Create the email subscription form&lt;/h2&gt;
          &lt;p&gt;Now that you’ve built the function, let’s call it from the email subscription form. The HTML for the email subscription form is very minimal.&lt;/p&gt;
          &lt;p&gt;All you need to do is call the function using the form action:&lt;/p&gt;
          &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;newsletter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/.netlify/functions/subscribe-email&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Your Email Address&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Subscribe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;Make sure you specific input name (“email”) and make sure it matches the information you parse from the event value using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;If you used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;submission-created&lt;/code&gt; trigger for your function like I did, you’ll need to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;form&amp;gt;&lt;/code&gt; field slightly by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-netlify=&quot;true&quot;&lt;/code&gt; to tell Netlify to process this form:&lt;/p&gt;
          &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;newsletter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-netlify=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Your Email Address&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Subscribe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;deploy-the-function&quot;&gt;Deploy the function&lt;/h2&gt;
          &lt;p&gt;Now that I’ve written my function, configured my netlify.toml file, and added my environment variables, everything is ready to go. Deploying is painless: just set up Netlify’s GitHub integration, and your function will be deployed when your project is pushed.&lt;/p&gt;
          &lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
          &lt;p&gt;It took less than 50 lines of code to create my own email subscription form including custom HTML and a serverless function to add new emails to my list. I wrote it all in HTML, CSS, and JavaScript, and everything is served from my domain. Plus, my website visitors get a nice experience whether they have JavaScript enabled or not, and it will still serve even if they have advanced privacy protection enabled.&lt;/p&gt;
        </content>
      </entry>
      <entry>
        <title>Why is Storytelling Important for B2B Marketing?</title>
        <link rel="alternate" href="https://andrewstiefel.com/storytelling-important-B2B-marketing/"/>
        <published>2022-06-22T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/storytelling-important-B2B-marketing</id>
        <summary>B2B storytelling helps unify groups of people around a shared narrative about what they need to accomplish.</summary>
        <content type="html">&lt;p&gt;Storytelling has been a buzzword in consumer marketing (B2C) for nearly a decade. And for a good reason. We, as humans, can’t help but tell stories. They are a critical way to convey information and build common ground.&lt;/p&gt;
          &lt;h2 id=&quot;storytelling-in-b2c-marketing&quot;&gt;Storytelling in B2C Marketing&lt;/h2&gt;
          &lt;p&gt;When I think about storytelling in marketing, emotional, high-production ads from companies like Apple, Coca-Cola, REI, and Starbucks come to mind.&lt;/p&gt;
          &lt;p&gt;You’ve probably seen examples, especially around the Super Bowl or other significant events. For instance, in this 2015 advertisement, Apple uses Robin Williams’ speech from &lt;em&gt;Dead Poets Society&lt;/em&gt;  to sell the iPad Air.&lt;/p&gt;
          &lt;div class=&quot;aspect-w-16 aspect-h-9&quot;&gt;
          &lt;iframe src=&quot;https://player.vimeo.com/video/112042156?h=c9de6161d7&amp;amp;title=0&amp;amp;byline=0&amp;amp;portrait=0&quot; class=&quot;w-full h-full&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; fullscreen; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
          &lt;/div&gt;
          &lt;p&gt;I may not want to run out and buy an iPad right after watching this, but I do remember the ad. And I feel inspired to create something. That feeling is now associated with Apple and their products. The Apple advertisement is an excellent example of why B2C marketers rely on storytelling to build customer connections and trust.&lt;/p&gt;
          &lt;h2 id=&quot;why-storytelling-matters-for-b2b-marketing&quot;&gt;Why Storytelling Matters for B2B Marketing&lt;/h2&gt;
          &lt;p&gt;For a long time, B2B marketing has held out as a place filled with dry white papers and ebooks waiting behind lead generation forms.&lt;/p&gt;
          &lt;p&gt;But storytelling is even more critical for B2B (enterprise) marketing.&lt;/p&gt;
          &lt;p&gt;Unlike most B2C products, in B2B marketing, you are selling to groups of people who need to make a decision together. These diverse groups of individuals have unique needs and problems that need to be solved. In many cases, you may sell to stakeholders whose problems you don’t directly solve (typically C-suite leadership).&lt;/p&gt;
          &lt;p&gt;The only way to win a deal with so many people is by first uniting everyone around a common cause that turns their problems into a set of shared obstacles to overcome.&lt;/p&gt;
          &lt;p&gt;A successful story must resonate with everyone in the buying process and establish a higher-level mission to solve together.&lt;/p&gt;
          &lt;p&gt;Successful B2B marketing starts with a strategic narrative — why your company exists and what you help solve. One of my favorite examples is Microsoft’s new mission under CEO Satya Nadella: to &lt;a href=&quot;https://www.microsoft.com/en-us/about&quot; title=&quot;Statista&quot; target=&quot;_blank&quot;&gt;empower every person and every organization on the planet to achieve more&lt;/a&gt;.&lt;/p&gt;
          &lt;p&gt;Microsoft can apply a simple story to consumer products and business tools. It can reframe whatever Microsoft sells around helping customers achieve a shared goal. That could include better healthcare outcomes, increasing student achievement, or building inclusive workplaces.&lt;/p&gt;
          &lt;h2 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h2&gt;
          &lt;p&gt;To summarize, storytelling is an essential component of B2B marketing because it is a powerful tool for persuasion that can help align groups of buyers around a shared narrative. With the right story, you can inspire confidence and build trust with your customers.&lt;/p&gt;
          &lt;p&gt;Let me know what you think on Twitter and LinkedIn.&lt;/p&gt;
        </content>
      </entry>
      <entry>
        <title>How to Differentiate Your Product When You Aren’t (Really) Different</title>
        <link rel="alternate" href="https://andrewstiefel.com/differentiate-product-competition/"/>
        <published>2022-06-21T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/differentiate-product-competition</id>
        <summary>Differentiation is about setting your product apart from your competition. But what if your product isn't really different?</summary>
        <content type="html">&lt;p&gt;Product differentiation communicates the unique qualities of your brand or product to distinguish it from your competition.&lt;/p&gt;
          &lt;p&gt;But what if your product isn’t unique?&lt;/p&gt;
          &lt;p&gt;Today plenty of products go to market without unique features. In the early days of B2B software, it was common to have features that no one else had in the market. Differentiation was reasonably straightforward. You would identify the unique benefit your feature provided for customers and craft a positioning statement around it.&lt;/p&gt;
          &lt;p&gt;In today’s technology landscape, differentiation with features is rare — everyone has similar functionality.&lt;/p&gt;
          &lt;p&gt;For example, you probably own a smartphone. &lt;a href=&quot;https://www.statista.com/statistics/266572/market-share-held-by-smartphone-platforms-in-the-united-states/&quot; title=&quot;Statista&quot; target=&quot;_blank&quot;&gt;Maybe you even own an Apple iPhone like more than 50% of U.S. consumers.&lt;/a&gt; But can you tell me the difference between the screen resolution on your iPhone versus a Samsung Galaxy? Are they even different?&lt;/p&gt;
          &lt;p&gt;Your opinion of that question is likely influenced by brand differentiation more than the unique qualities of either phone. Do you know how many pixels are in a Retina display? Or how that’s different than the screen on a Samsung phone?&lt;/p&gt;
          &lt;p&gt;It’s easier than ever for competitors to copy your product. But does that mean you shouldn’t build a great product? Nope.&lt;/p&gt;
          &lt;p&gt;It just means that a great product is table stakes.&lt;/p&gt;
          &lt;p&gt;Companies that are beating their competitors have a great product and build a unique market position.&lt;/p&gt;
          &lt;p&gt;Here’s how you differentiate your product from the competition:&lt;/p&gt;
          &lt;ol&gt;
          &lt;li&gt;&lt;a href=&quot;#target-a-niche-market-segment&quot;&gt;Target a niche market segment&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#tell-a-differentiated-story&quot;&gt;Tell a differentiated story&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#provide-valuable-education&quot;&gt;Provide valuable education&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#build-a-community-around-your-vision&quot;&gt;Build a community around your vision&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#cultivate-a-partner-ecosystem&quot;&gt;Cultivate a partner ecosystem&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#amplify-employee-advocacy&quot;&gt;Amplify employee advocacy&lt;/a&gt;&lt;/li&gt;
          &lt;/ol&gt;
          &lt;p&gt;Each takes time to build. But don’t get intimidated—that’s the point. Your competition cannot easily duplicate your work.&lt;/p&gt;
          &lt;p&gt;Let’s explore how you can differentiate your product even when your products don’t have any unique features.&lt;/p&gt;
          &lt;h2 id=&quot;target-a-niche-market-segment&quot;&gt;Target a niche market segment&lt;/h2&gt;
          &lt;p&gt;The first mistake many companies make is targeting too large a market. When you’re trying to please everyone, you please no one.&lt;/p&gt;
          &lt;p&gt;Success starts by narrowing in on a niche in the market that isn’t being served well by current solutions. If you’re familiar with &lt;a href=&quot;https://amzn.to/3Oy7LtO&quot; title=&quot;Amazon&quot; id=&quot;clickAff&quot; target=&quot;_blank&quot;&gt;Geoffrey Moore’s book &lt;em&gt;Crossing the Chasm&lt;/em&gt;&lt;/a&gt;, you might recognize this approach by another name – “land and expand.”&lt;/p&gt;
          &lt;p&gt;You may have to niche down a few times to find the right fit in the market, progressively narrowing in on a smaller and smaller customer niche each time.&lt;/p&gt;
          &lt;p&gt;But wait, you might ask, don’t I want a bigger audience?&lt;/p&gt;
          &lt;p&gt;Eventually, maybe you will. But not when you are struggling to gain traction with your first customers. The more specific and targeted you can be with your positioning and messaging, the more your product will resonate with your customers.&lt;/p&gt;
          &lt;p&gt;I think ConvertKit is an excellent example of the niche-down approach. When Nathan Barry started the company, he targeted anyone who needed email marketing. As a result, he was up against heavyweights like MailChimp, ActiveCampaign, and others. And he was failing to gain traction.&lt;/p&gt;
          &lt;p&gt;Instead of giving up, he narrowed his market to target professional bloggers and worked to address their problems. He gradually expanded to other digital creators as he gained traction with bloggers. Today the business operates at over $100,000 monthly recurring revenue (MRR).&lt;/p&gt;
          &lt;p&gt;&lt;a href=&quot;https://twitter.com/nathanbarry/status/1512782207508643845&quot;&gt;Check out Nathan Barry’s Twitter thread&lt;/a&gt;c for more about his journey building ConvertKit.&lt;/p&gt;
          &lt;h2 id=&quot;tell-a-differentiated-story&quot;&gt;Tell a differentiated story&lt;/h2&gt;
          &lt;p&gt;When trying to differentiate your business, the most crucial question is not “what makes our product better than others?” Instead, it’s “why should customers care?”&lt;/p&gt;
          &lt;p&gt;The most successful businesses don’t just sell products; they tell stories and motivate customers to buy into their vision. Instead of discussing what features make your product so great, talk about why it matters. Great stories are never about your products or services. They are about how people use your products and services to improve their lives.&lt;/p&gt;
          &lt;p&gt;If you are a B2B company, tell the story about how your customers use your products or services uniquely. If you are B2C, tell the story of how consumers use your products or services to improve their lives.&lt;/p&gt;
          &lt;p&gt;Whatever story you tell, ensure it provides a unique point of view on the market you are targeting. For example, how do you understand the market? What is your unique take on how to address challenges within your space?&lt;/p&gt;
          &lt;p&gt;&lt;a href=&quot;https://www.drift.com/&quot; title=&quot;Drift&quot; target=&quot;_blank&quot;&gt;Drift&lt;/a&gt; is an excellent example of how this can work in practice. They offer chatbots for customer service. But instead of focusing on automation, they invented a new category for their product named “conversational marketing.” They worked to build leadership within that term and created a stronghold in the market despite competition from other vendors in the space.&lt;/p&gt;
          &lt;p&gt;Check out this HubSpot blog post with examples of &lt;a href=&quot;https://blog.hubspot.com/insiders/branding-differentiation&quot; title=&quot;Hubspot&quot; target=&quot;_blank&quot;&gt;10 companies that brilliantly differentiated themselves from the competition&lt;/a&gt;.&lt;/p&gt;
          &lt;h2 id=&quot;provide-valuable-education&quot;&gt;Provide valuable education&lt;/h2&gt;
          &lt;p&gt;Educate your audience about the trends and challenges you see in the market, how they can solve their problems, and how to get the most value from your product to solve those challenges.&lt;/p&gt;
          &lt;p&gt;The key is to help your customers. Whatever you create (blog, video, ebook, podcast, etc.) should help your customers learn something new. You can even take this further and provide certification or verification that they have learned a specific skill. For example, how to use your product (Microsoft Azure Fundamentals) or a particular set of skills (HubSpot Inbound Marketing).&lt;/p&gt;
          &lt;p&gt;My favorite example of education is &lt;a href=&quot;https://buffer.com/&quot; title=&quot;Buffer&quot; target=&quot;_blank&quot;&gt;Buffer&lt;/a&gt;, especially in its early days as a company around 2012–2016. Their blog consistently published valuable content about social media strategy and tactics. Then they shared it via newsletter and social media.&lt;/p&gt;
          &lt;p&gt;Personally, Buffer’s blog was invaluable when I was starting a career as a social media manager. At that time, there were no college programs or classes for digital marketing. And there weren’t that many other people who had much experience yet either. Nevertheless, we were all learning on the job. Buffer filled a gap by breaking down trends from their data and offering actionable tips daily.&lt;/p&gt;
          &lt;h2 id=&quot;build-a-community-around-your-vision&quot;&gt;Build a community around your vision&lt;/h2&gt;
          &lt;p&gt;You need to do more than publish content on your blog. It would be best if you found ways to connect with potential customers one-on-one to understand their problems and how you can help.&lt;/p&gt;
          &lt;p&gt;One of the best ways to do this is by building a community around your vision. Find where your audience interacts online or in person and gradually join the conversation. If you’re already creating valuable educational content, it should be easy to chime in as questions arise. Eventually, you’ll want your audience to buy in and start sharing your vision.&lt;/p&gt;
          &lt;p&gt;Remember that a community can be centralized (Slack, Discord, Facebook Groups) or decentralized (conversations and hashtags on social media).&lt;/p&gt;
          &lt;p&gt;Different companies take different approaches depending on what they want to accomplish. For example, Nike, REI, and Starbucks have massive audiences online. As a result, they can rely on social media to amplify their brand messages and drive conversation around their brands. (The REI OptOutside campaign is a great example – people opt-in and interact around it every year.)&lt;/p&gt;
          &lt;p&gt;Other brands rely on bringing people together in a centralized space. For example, developer communities often organize around Slack or Discord as a place to discuss problems and share ideas – the VueJS community is a great example.&lt;/p&gt;
          &lt;h2 id=&quot;cultivate-a-partner-ecosystem&quot;&gt;Cultivate a partner ecosystem&lt;/h2&gt;
          &lt;p&gt;In addition to building a network with your customers, you should try to recruit partners who share your vision or philosophy. There are three main ways that partners can help differentiate your product:&lt;/p&gt;
          &lt;p&gt;&lt;strong&gt;1) Provide value-added services&lt;/strong&gt;
          Partners function as an extension of your organization. By partnering with other companies, you can offer added value to your customers by leveraging their expertise. For example, your partners could provide anything from training and support to custom development and integration services. By providing additional services, partners can help your customers realize the full value of their investment in your product.&lt;/p&gt;
          &lt;p&gt;&lt;strong&gt;2) Provide product extensions and integrations&lt;/strong&gt;
          Partners can provide extensions to your core product, adding new functionality and features that complement what you build. Integrations help customers build seamless workflows that address their specific use cases.&lt;/p&gt;
          &lt;p&gt;&lt;strong&gt;3) Generate co-sell opportunities&lt;/strong&gt;
          In addition to extending functionality through integrations, partners can generate new sales opportunities by selling your product and theirs together as a bundle. Selling together is especially effective when the two products are complementary rather than competitive (e.g., CRM + marketing automation).&lt;/p&gt;
          &lt;p&gt;Once you start noticing how common partnerships are in the market, it’s hard to stop seeing them everywhere. One recent example I came across recently as a business owner was a bank – &lt;a href=&quot;https://www.novo.co/&quot; title=&quot;Novo&quot; target=&quot;_blank&quot;&gt;Novo&lt;/a&gt;. They help small business owners get started with business checking accounts.&lt;/p&gt;
          &lt;p&gt;Novo has built a partner ecosystem that helps new business owners get started faster. They offer discounts and integrations with critical partners like Quickbooks for invoicing, Stripe for collecting payments, and HubSpot as a CRM. New customers get access to these additional perks. By building a partner ecosystem, Novo helps drive business for their partners and helps keep customers invested in using Novo as their bank of choice.&lt;/p&gt;
          &lt;h2 id=&quot;amplify-employee-advocacy&quot;&gt;Amplify employee advocacy&lt;/h2&gt;
          &lt;p&gt;You automatically extend your reach when your employees advocate for your brand through their social ecosystem.&lt;/p&gt;
          &lt;p&gt;But more importantly, you can increase trust.&lt;/p&gt;
          &lt;p&gt;Customers often look to your employees as the best source of information about your company and products. According to research from Edelman, &lt;a href=&quot;http://www.edelman.com/2015-edelman-trust-barometer/trust-across-industries/trust-in-employee-engagement/&quot; title=&quot;Edelman&quot; target=&quot;_blank&quot;&gt;68% of consumers say they trust technical experts from a company&lt;/a&gt; over the CEO or marketing material.&lt;/p&gt;
          &lt;p&gt;So why do customers trust employees?&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;They know more about your company and your products than anyone else&lt;/li&gt;
          &lt;li&gt;They know what your product can do – and what it can’t do&lt;/li&gt;
          &lt;li&gt;They know your competition and category and how you fit in&lt;/li&gt;
          &lt;li&gt;And most importantly: they know what matters to your customers&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;To succeed, you must first build a culture of trust and sharing. Unfortunately, most companies try to buy some software, implement it, and then give up when employees are less than enthusiastic about it.&lt;/p&gt;
          &lt;p&gt;A winning example, however, is &lt;a href=&quot;https://www.linkedin.com/company/refine-labs/&quot; title=&quot;LinkedIn&quot; target=&quot;_blank&quot;&gt;Refine Labs&lt;/a&gt;.&lt;/p&gt;
          &lt;p&gt;The entire company is active on LinkedIn, sharing insights and commenting on posts about demand generation. Their success isn’t the result of fancy tools — the company’s CEO, Chris Walker, built advocacy into the culture through his leadership, hiring, and coaching of the team. It’s part of their core identity.&lt;/p&gt;
          &lt;h2 id=&quot;the-result-a-differentiated-brand&quot;&gt;The result? A differentiated brand&lt;/h2&gt;
          &lt;p&gt;The strategies above take time and a dedication to implement. That’s why they are so successful. Unfortunately, most companies fail to sustain the needed work to build a differentiated product and brand.&lt;/p&gt;
          &lt;p&gt;So now that you better understand how to differentiate your product, it’s time for action! If your product has no unique features, you can use the strategies above to differentiate your product and company from the competition.&lt;/p&gt;
          &lt;p&gt;And if you do have some unique features? Well, don’t stop promoting those just yet! You can still use these strategies to show off what makes your company special.&lt;/p&gt;
          &lt;p&gt;Let me know what you think on &lt;a href=&quot;https://twitter.com/intent/tweet?text=https://andrewstiefel.com/differentiate-product-competition/&quot; title=&quot;Twitter&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/shareArticle?mini=true&amp;amp;url=https%3A//andrewstiefel.com&amp;amp;title=&amp;amp;summary=&amp;amp;source=&quot; title=&quot;LinkedIn&quot; target=&quot;_blank&quot;&gt;LinkedIn&lt;/a&gt;. Thanks for reading!&lt;/p&gt;
        </content>
      </entry>
      <entry>
        <title>Networking Tips for Remote and Hybrid Work</title>
        <link rel="alternate" href="https://andrewstiefel.com/networking-tips-remote-hybrid-job/"/>
        <published>2022-05-25T00:00:00-07:00</published>
        <id>https://andrewstiefel.com/networking-tips-remote-hybrid-job</id>
        <summary>Networking is always a challenge when starting a new job. It's even more critical for success in a remote or hybrid workplace.</summary>
        <content type="html">&lt;p&gt;You’ve finally landed that dream job — and it’s going to involve some (or a lot) of remote work. Maybe it’s your first job, or maybe you just took a position with a new company. Or, like the rest of the world, you and your team have transitioned to working remotely during the pandemic.&lt;/p&gt;
          &lt;p&gt;Regardless of the reason, you are now tasked with building relationships with co-workers you rarely see in person.&lt;/p&gt;
          &lt;p&gt;I had to onboard remotely twice during the pandemic. It was a much more challenging experience than starting in person. The first weeks can feel lonely, and it takes time to build the visibility and trust you need to drive projects forward.&lt;/p&gt;
          &lt;p&gt;That said, I love working remotely. And I’ve learned a few things along the way.&lt;/p&gt;
          &lt;p&gt;Here’s how you can network effectively in an era of remote and hybrid work:&lt;/p&gt;
          &lt;ol&gt;
          &lt;li&gt;&lt;a href=&quot;#polish-your-linkedin-profile&quot;&gt;Polish your LinkedIn profile&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#schedule-virtual-coffee-meetings&quot;&gt;Schedule virtual coffee meettings&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#engage-with-professional-communities&quot;&gt;Engage with professional communities&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#share-what-you-learn&quot;&gt;Share what your learn&lt;/a&gt;&lt;/li&gt;
          &lt;/ol&gt;
          &lt;h2 id=&quot;polish-your-linkedin-profile&quot;&gt;Polish your LinkedIn profile&lt;/h2&gt;
          &lt;p&gt;Your LinkedIn profile isn’t just for finding a job — it’s also your first impression when meeting and networking with colleagues at your new company.&lt;/p&gt;
          &lt;p&gt;When I worked in person, I rarely checked my colleagues’ LinkedIn profiles. And why would I? We could chat at our desks or run into each other in the office.&lt;/p&gt;
          &lt;p&gt;After onboarding remotely twice during the pandemic, I can say with certainty that your LinkedIn profile matters more than even your resume. It’s the first place people look to understand who you are and what you’re about.&lt;/p&gt;
          &lt;p&gt;I use LinkedIn before scheduling meetings with people I haven’t met yet, or during meetings when I encounter someone new, so I can quickly identify their role and what might be important to them.&lt;/p&gt;
          &lt;p&gt;Here are some tips on how to optimize your LinkedIn profile:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Write an engaging introduction section&lt;/strong&gt; that explains your career path, professional interests, and scope of responsibilities&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Update your profile regularly&lt;/strong&gt; so that it reflects your current role at the company, key successes and projects, and relevant skills&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Make a great first impression&lt;/strong&gt; with your LinkedIn headshot — it doesn’t need to be fancy but make sure your face is clearly visible&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;If you’re looking for more tips, I suggest browsing this &lt;a href=&quot;https://www.jobscan.co/linkedin-profile-writing-guide&quot;&gt;comprehensive LinkedIn profile writing guide&lt;/a&gt; from the team at Jobscan.&lt;/p&gt;
          &lt;h2 id=&quot;schedule-virtual-coffee-meetings&quot;&gt;Schedule virtual coffee meetings&lt;/h2&gt;
          &lt;p&gt;When you first join a company, schedule 1:1 meetings with various stakeholders. I treat these like informal coffee chats and use them as an opportunity to learn about what people are working on, what their priorities are, and how I might work with them on shared goals.&lt;/p&gt;
          &lt;p&gt;Be proactive about introductions – don’t wait for others to introduce themselves. You should focus on meeting with your peers just as much, or maybe more, than meeting with different leaders.&lt;/p&gt;
          &lt;p&gt;Your goal here is to make sure that people know who you are and what your role is at the company. This helps build relationships early on so that when there are opportunities for collaboration or mentorship later down the road, there will already be a foundation of trust between you and other members of your team.&lt;/p&gt;
          &lt;p&gt;Don’t be afraid to ask questions and share ideas! Starting a new role is an exciting time to learn. And most people enjoy being consulted as experts in their areas and are willing to share what they know.&lt;/p&gt;
          &lt;p&gt;Once you’ve established a relationship with someone, it’s important to keep up the communication even when you aren’t working together. Connect over Slack or Teams by sharing interesting articles or news, checking in on how they are doing, or chatting about shared interests.&lt;/p&gt;
          &lt;p&gt;One of the best things you can do is recognize when someone else is doing something great. Send an email thanking them for their work and letting them know how much you appreciate what they did — especially if it wasn’t necessarily part of their job description. If you’re announcing a major event to your team via Slack or Teams, make sure to tag and thank them in the post.&lt;/p&gt;
          &lt;h2 id=&quot;engage-with-professional-communities&quot;&gt;Engage with professional communities&lt;/h2&gt;
          &lt;p&gt;The rise of professional communities on Slack, Teams, Discord, and other platforms has created many opportunities to connect with other professionals.&lt;/p&gt;
          &lt;p&gt;Find out where your colleagues hang out online and join their communities. Personally, I use the &lt;a href=&quot;https://www.productmarketingalliance.com/join-slack/&quot;&gt;Product Marketing Alliance (PMA) Slack group&lt;/a&gt; as a way to read about product marketing topics, ask questions, and connect with other professionals by answering questions when I can.&lt;/p&gt;
          &lt;p&gt;Seek opportunities to attend or present at conferences. These are great opportunities to meet other professionals face-to-face and share experiences. They can even be a great way to network with your team or other people at your company if you can attend as a group.&lt;/p&gt;
          &lt;h2 id=&quot;share-what-you-learn&quot;&gt;Share what you learn&lt;/h2&gt;
          &lt;p&gt;Even though I worked as a content marketer and social media manager for several years, I’ve always been a bit shy when it comes to writing on my blog or using my social media profiles.&lt;/p&gt;
          &lt;p&gt;After starting two remote roles, I now recognize the importance of creating and sharing valuable content in a remote and hybrid work environment. It’s a way to build reputation and network within your organization just as much as it’s a way to network outside your company.&lt;/p&gt;
          &lt;p&gt;Be active on social media platforms like LinkedIn or Twitter. Or even better, start a blog, publish a newsletter, or create videos. The goal is to get your voice out in public and get your ideas in front of your co-workers regularly.&lt;/p&gt;
          &lt;p&gt;Sharing what you learn in public is a great way to build your reputation and connect with people who might share some of your interests. The more people see your name associated with valuable content, the more likely they are to trust you as an expert and reach out when they have questions or need advice from someone with your background.&lt;/p&gt;
          &lt;p&gt;At first, don’t spend time worrying about having a unique angle, beat, or perspective. Just start and tweak as you go. If you’re not sure what to write about, try one of these prompts to get started:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;Today I learned how to…&lt;/li&gt;
          &lt;li&gt;My favorite tools for X are…&lt;/li&gt;
          &lt;li&gt;I was thinking about…&lt;/li&gt;
          &lt;/ul&gt;
          &lt;p&gt;The last prompt is actually how this post started. I was thinking about how to better network while onboarding into my new role and decided to write this blog post as a way of teaching myself the best way to get started.&lt;/p&gt;
          &lt;h2 id=&quot;be-patient&quot;&gt;Be patient&lt;/h2&gt;
          &lt;p&gt;Finally, be patient with yourself (and with others). Starting a new job is exciting, and you want to get going right away. Building new relationships takes time, and it’s going to feel lonely at first. If you work remotely, you will have to be ok leading your learning, networking, and career building.&lt;/p&gt;
          &lt;p&gt;On the flip side, it is also empowering. Remote and hybrid work environments offer greater flexibility, work-life balance, and autonomy. I love taking a few minutes to grab a cup of coffee from my kitchen, pet my rabbits, and take in the view of my neighborhood rather than the concrete downtown streets before heading back to work.&lt;/p&gt;
          &lt;p&gt;Please let me know what you think on Twitter or LinkedIn. Thanks for reading!&lt;/p&gt;
        </content>
      </entry>
      <entry>
        <title>How to Style an Atom Feed with XSLT</title>
        <link rel="alternate" href="https://andrewstiefel.com/style-atom-xsl/"/>
        <published>2022-01-24T00:00:00-08:00</published>
        <id>https://andrewstiefel.com/style-atom-xsl</id>
        <summary>Create a seamless user experience for your Atom or RSS feed with XLST stylesheets.</summary>
        <content type="html">&lt;p&gt;Maybe it’s nostalgia for the early web, but I love web feeds as a tool for following and reading content. Feeds are privacy-first and put the reader in control: you can opt out any time, choose your tool for reading, and organize them in any way you want.&lt;/p&gt;
          &lt;p&gt;But the UX experience is terrible.&lt;/p&gt;
          &lt;p&gt;Web feeds are meant to be machine-readable, so most users follow a link to an RSS or Atom feed and end up looking at something like this:&lt;/p&gt;
          &lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_725/https://andrewstiefel.com/assets/img/raw-atom-rss.png&quot; srcset=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_320/https://andrewstiefel.com/assets/img/raw-atom-rss.png 320w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_602/https://andrewstiefel.com/assets/img/raw-atom-rss.png 602w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_760/https://andrewstiefel.com/assets/img/raw-atom-rss.png 760w&quot; sizes=&quot;(min-width: 50rem) 50rem, 90vw&quot; data-lightbox=&quot;&quot; data-full=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/q_auto,f_auto/https://andrewstiefel.com/assets/img/raw-atom-rss.png&quot; alt=&quot;Raw RSS or Atom feed&quot; width=&quot;760&quot; height=&quot;483&quot; crossorigin=&quot;anonymous&quot; class=&quot;dark:brightness-75 cursor-pointer&quot; /&gt;&lt;/p&gt;
          &lt;p&gt;This doesn’t have to be the case. RSS and Atom feeds can be human-readable with a little extra work. &lt;a href=&quot;/feed.xml&quot; target=&quot;_blank&quot; data-fathom=&quot;RSS subscription&quot;&gt;Here’s an example from my website&lt;/a&gt;. It’s simple and clean and provides some essential instructions on how to get started:&lt;/p&gt;
          &lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_725/https://andrewstiefel.com/assets/img/human-readable-atom-feed.png&quot; srcset=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_320/https://andrewstiefel.com/assets/img/human-readable-atom-feed.png 320w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_602/https://andrewstiefel.com/assets/img/human-readable-atom-feed.png 602w, https://res.cloudinary.com/andrewstiefel/image/fetch/c_limit,f_auto,q_auto,w_760/https://andrewstiefel.com/assets/img/human-readable-atom-feed.png 760w&quot; sizes=&quot;(min-width: 50rem) 50rem, 90vw&quot; data-lightbox=&quot;&quot; data-full=&quot;https://res.cloudinary.com/andrewstiefel/image/fetch/q_auto,f_auto/https://andrewstiefel.com/assets/img/human-readable-atom-feed.png&quot; alt=&quot;Human-readable Atom or RSS feed&quot; width=&quot;760&quot; height=&quot;483&quot; crossorigin=&quot;anonymous&quot; class=&quot;dark:brightness-75 cursor-pointer&quot; /&gt;&lt;/p&gt;
          &lt;p&gt;Let’s explore how to implement this with Atom and an XSLT stylesheet.&lt;/p&gt;
          &lt;h2 id=&quot;why-atom-and-not-rss&quot;&gt;Why Atom, and not RSS?&lt;/h2&gt;
          &lt;p&gt;Great question, and one I don’t intend to answer fully here given the vast amounts of writing on this topic already. In short, Atom is a better format.&lt;/p&gt;
          &lt;p&gt;Specifically, I wanted the following:&lt;/p&gt;
          &lt;ul&gt;
          &lt;li&gt;Support for a full content payload in the feed&lt;/li&gt;
          &lt;li&gt;Wide support across feed readers&lt;/li&gt;
          &lt;li&gt;Extensibility and future-proof format&lt;/li&gt;
          &lt;/ul&gt;
          &lt;h2 id=&quot;first-create-the-feed-in-jekyll&quot;&gt;First, create the feed in Jekyll&lt;/h2&gt;
          &lt;p&gt;&lt;em&gt;If you’re using a different tech stack to create your website, you can &lt;a href=&quot;#create-the-xsl-file-to-style-the-feed&quot;&gt;skip this section&lt;/a&gt; and jump ahead to the part about creating an XSLT stylesheet.&lt;/em&gt;&lt;/p&gt;
          &lt;p&gt;You can either use the excellent &lt;a href=&quot;https://github.com/jekyll/jekyll-feed&quot; title=&quot;Jekyll Feed&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jekyll Feed plugin&lt;/a&gt;, or create your own template using liquid tags and save it in your project as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feed.xml&lt;/code&gt;.&lt;/p&gt;
          &lt;p&gt;I chose to create my own template, so I could incorporate some additional markup. You can see my version below:&lt;/p&gt;
          &lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
          ---
          &lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml-stylesheet href=&quot;/assets/css/feed.xsl&quot; type=&quot;text/xsl&quot;?&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;feed&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2005/Atom&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ site.title }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.url }}{{ site.baseurl }}/feed.xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.url }}{{ site.baseurl }}/&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alternate&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;subtitle&amp;gt;&lt;/span&gt;{{ site.description }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/subtitle&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;{{ site.time | date_to_xmlschema }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;{{ site.url }}/&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;author&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;{{ site.author.name }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;email&amp;gt;&lt;/span&gt;{{ site.author.email }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/email&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;rights&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Copyright © {{ site.time | date: &quot;%Y&quot; }} {{ site.author }}. All rights reserved.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/rights&amp;gt;&lt;/span&gt;
          {% for post in site.posts %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;entry&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alternate&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.url }}{{ post.url }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;{{ post.date | date_to_xmlschema }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;{{ site.url }}{{ site.baseurl }}{{ post.id }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;{{ post.description }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ post.content | xml_escape }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/entry&amp;gt;&lt;/span&gt;
          {% endfor %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/feed&amp;gt;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;p&gt;&lt;strong&gt;Note on YAML Front Matter block:&lt;/strong&gt; It’s important to leave the dashes at the top of the file. This is necessary because Jekyll will not process a page with Liquid unless there is a YAML block at the top of the file.&lt;/p&gt;
          &lt;p&gt;&lt;strong&gt;Enable auto-discovery:&lt;/strong&gt; Make sure you add the appropriate meta tag to support automated discovery of your feed. Place the following code somewhere in your template’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section to output the necessary metadata:&lt;/p&gt;
          &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;application/atom+xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alternate&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.url }}/feed.xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ site.title }}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;create-the-xsl-file-to-style-the-feed&quot;&gt;Create the XSL file to style the feed&lt;/h2&gt;
          &lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms759096%28v=vs.85%29&quot; title=&quot;What Is XSLT?&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;We can use XSLT to style our XML&lt;/a&gt;. This makes our feeds more human-readable while supporting bots, aggregators, and search engines.&lt;/p&gt;
          &lt;p&gt;You’ll notice some similarities to HTML and CSS in the example below, but with a few semantic changes and special attributes.&lt;/p&gt;
          &lt;p&gt;First, you can place your CSS in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag like normal. You can also use some standard HTML markup like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
          &lt;p&gt;There are a few special elements you can use, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;xsl:apply-templates&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;xsl:value-of&amp;gt;&lt;/code&gt;. I won’t cover these in detail during this tutorial, but W3Schools maintains &lt;a href=&quot;https://www.w3schools.com/xml/xsl_elementref.asp&quot; title=&quot;XSLT Reference&quot;&gt;a great XSLT reference&lt;/a&gt; if you want to learn about all these special elements.&lt;/p&gt;
          &lt;p&gt;This Github Gist shows an &lt;a href=&quot;https://gist.github.com/andrewstiefel/57a0a400aa2deb6c9fe18c6da4e16e0f&quot; target=&quot;blank&quot; rel=&quot;noopener noreferrer&quot;&gt;example of the XLST stylesheet I created for my website&lt;/a&gt;. I wrote some basic CSS styles to format it, but you could even tap into your site’s primary CSS file to keep the styling consistent.&lt;/p&gt;
          &lt;p&gt;Save your file as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feed.xsl&lt;/code&gt; and add the tag below to your XML file. Make sure the href tag points to the correct location and file name for your website.&lt;/p&gt;
          &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml-stylesheet href=&quot;/feed.xsl&quot; type=&quot;text/xsl&quot;?&amp;gt;
          &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
          &lt;p&gt;With a little extra care and attention, we can improve the experience of using RSS and Atom feeds across the web. Now when you visit my feed, you are greeted with an explanation of how to get started and a formatted recap of my latest posts.&lt;/p&gt;
          &lt;p&gt;I made my own XML template and XSLT stylesheet based on the examples above. You can &lt;a href=&quot;https://andrewstiefel.com/feed.xml&quot; title=&quot;Andrew Stiefel&apos;s Feed&quot; target=&quot;_blank&quot; data-fathom=&quot;RSS subscription&quot;&gt;see it in action here&lt;/a&gt; or &lt;a href=&quot;https://gist.github.com/andrewstiefel/57a0a400aa2deb6c9fe18c6da4e16e0f&quot; title=&quot;Github Gist&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;download a version&lt;/a&gt; to adapt for your website.&lt;/p&gt;
          &lt;p&gt;Thanks for reading!&lt;/p&gt;
          &lt;h2 id=&quot;additional-reading&quot;&gt;Additional Reading&lt;/h2&gt;
          &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://interconnected.org/home/2020/07/29/improving_rss&quot; title=&quot;Interconnected by Matt Webb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;How would I improve RSS? Three ideas (Interconnected by Matt Webb)&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://lepture.com/en/2019/rss-style-with-xsl&quot; title=&quot;Just Lepture&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;How to style an RSS feed (Just Lepture)&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://natclark.com/tutorials/xslt-style-rss-feed/&quot; title=&quot;Nat Clark&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Styling an RSS Feed with XSLT (Nat Clark)&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;https://aboutfeeds.com/&quot; title=&quot;About Feeds&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;About Feeds&lt;/a&gt;&lt;/li&gt;
          &lt;/ul&gt;
        </content>
      </entry>
    </feed>