<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ghost</title>
    <description>The latest articles on Forem by Ghost (@tryghost).</description>
    <link>https://forem.com/tryghost</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5895%2F28bb1c4e-f5f3-4e7a-8a31-b5469f18c15c.png</url>
      <title>Forem: Ghost</title>
      <link>https://forem.com/tryghost</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tryghost"/>
    <language>en</language>
    <item>
      <title>Install Node on macOS, Windows, and Linux</title>
      <dc:creator>Ryan Feigenbaum</dc:creator>
      <pubDate>Mon, 01 May 2023 13:40:20 +0000</pubDate>
      <link>https://forem.com/tryghost/install-node-on-macos-windows-and-linux-3o8a</link>
      <guid>https://forem.com/tryghost/install-node-on-macos-windows-and-linux-3o8a</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RZ1LNMhbWa0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As the saying goes, "Every journey begins with a single step." In the world of web development, that first step is often Node. This tutorial explains what Node is and how to install it on your system. Once installed, you’ll be ready to run Ghost locally and begin your journey of creating a custom Ghost theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Node?
&lt;/h2&gt;

&lt;p&gt;Usually, JavaScript runs in your browser, but in 2009, Ryan Dahl had the idea to create a runtime — Node — so that JavaScript could run independently of it. Today, nearly all modern web development uses Node in some way. Ghost runs entirely on Node, and, fun fact, when Ghost first came out in 2013, it ran on Node v0.10. Not even version 1!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node Package Manager&lt;/strong&gt; (NPM) is included with every installation of Node. It’s a command-line tool for installing, managing, and sharing JavaScript code. For example, use NPM to &lt;a href="https://ghost.org/docs/install/local/?ref=ghost.org" rel="noopener noreferrer"&gt;install Ghost&lt;/a&gt; or &lt;a href="https://www.npmjs.com/?ref=ghost.org" rel="noopener noreferrer"&gt;any of these other 3 million-plus packages&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Node on macOS
&lt;/h2&gt;

&lt;p&gt;To install Node on macOS, open up a browser and navigate to &lt;a href="https://nodejs.org/?ref=ghost.org" rel="noopener noreferrer"&gt;the official Node website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;LTS&lt;/strong&gt; version on the left and download the installer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0rcbo249yd5bq2zqgod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0rcbo249yd5bq2zqgod.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the installer, choosing the default options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ucaqs5foq4baumbgws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ucaqs5foq4baumbgws.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the installer completes, confirm Node is installed by opening a terminal and running &lt;code&gt;node -v&lt;/code&gt;. The terminal will output the current version of Node that’s installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqx5g4v2oe4f5llztopop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqx5g4v2oe4f5llztopop.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all there is to it. You’re now ready to start using Node 🥳&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🏋️ If you’re looking for more control over your Node installation or foresee wanting to switch Node versions, check out the instructions for using &lt;code&gt;nvm&lt;/code&gt; in the Linux section below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Install Node on Windows
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/?ref=ghost.org" rel="noopener noreferrer"&gt;Go to Node’s official page.&lt;/a&gt; Click on the &lt;strong&gt;LTS&lt;/strong&gt; version on the left and download the installer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcx49xsiihfj7s7jvldj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcx49xsiihfj7s7jvldj.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the installer prompts. It’s recommended to check “Yes” on the &lt;strong&gt;Tools for Native Modules&lt;/strong&gt; screen to ensure you don’t run into potential compatibility issues in the future. Checking this box runs an additional installation script.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvl3xsovzfdwr2o5pnff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvl3xsovzfdwr2o5pnff.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installation, confirm that Node is installed correctly by opening your terminal and running &lt;code&gt;node -v&lt;/code&gt;. The command will output Node’s version number to the console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6ofssoh7fer3fn5qxkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6ofssoh7fer3fn5qxkx.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re now ready to start using Node 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Node on WSL
&lt;/h2&gt;

&lt;p&gt;Windows users have another option for installing Node: &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install?ref=ghost.org" rel="noopener noreferrer"&gt;Windows Subsystem for Linux or WSL&lt;/a&gt;. WSL allows you to run a Linux environment on your Windows machine. Because most web development happens in Linux environments, the benefits of using WSL are that you’ll have fewer problems, an easier time following tutorials, and more tools at your disposal. The downside is that it takes a few additional steps to configure and install.&lt;/p&gt;

&lt;p&gt;Open a terminal as an &lt;strong&gt;administrator&lt;/strong&gt; by right-clicking on the application and choosing “Run as administrator.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zjsrt2zug9gk2emwfsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zjsrt2zug9gk2emwfsh.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows will install WSL. After installation is complete, restart your machine. Open your terminal and select &lt;strong&gt;Ubuntu&lt;/strong&gt; , the WSL distribution installed by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2zopil7kidx3fr7apwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2zopil7kidx3fr7apwj.png" alt="Install Node on macOS, Windows, and Linux" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re now on Linux on Windows. Pretty cool! To install Node, follow the instructions in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Node on Linux
&lt;/h2&gt;

&lt;p&gt;The best way to install Node on Linux is to use &lt;strong&gt;&lt;a href="https://github.com/nvm-sh/nvm?ref=ghost.org" rel="noopener noreferrer"&gt;Node version manager&lt;/a&gt;&lt;/strong&gt;&lt;a href="https://github.com/nvm-sh/nvm?ref=ghost.org" rel="noopener noreferrer"&gt;(nvm)&lt;/a&gt;. It’s a script that makes it easy to install up-to-date Node versions and switch between them.&lt;/p&gt;

&lt;p&gt;Open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Close your terminal and reopen it. Check that nvm is installed correctly by running &lt;code&gt;command -v nvm&lt;/code&gt;. This command should output &lt;code&gt;nvm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, &lt;a href="https://ghost.org/docs/faq/node-versions/?ref=ghost.org" rel="noopener noreferrer"&gt;check which version of Node Ghost currently supports&lt;/a&gt;. Install it with the following command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once installation is complete, close and reopen your terminal. Check that Node is installed correctly with &lt;code&gt;node -v&lt;/code&gt;.  If it returns the current version installed, then you’re in business and ready to start using Node 🥳&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;nvm&lt;/code&gt; works great when using Ghost locally, but it's not recommended for production. &lt;a href="https://ghost.org/docs/faq/using-nvm/?ref=ghost.org" rel="noopener noreferrer"&gt;See our FAQ for more info.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;It’s hard to overstate how omnipresent Node is in the current web development ecosystem. Name a website or app and somewhere along the line, they’re probably using Node. Installing it on your system is the first step in creating an amazing app or a super sick custom Ghost theme.&lt;/p&gt;

&lt;p&gt;Your next step is to install Ghost locally, which should be a breeze now that Node is installed 😉&lt;/p&gt;

&lt;p&gt;And we have lots more resources to help you build a custom theme:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.notion.so/070a4e323c154a81b60595285e583ab6?ref=ghost.org" rel="noopener noreferrer"&gt;The official Ghost docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.notion.so/379479fa194b496ca4b917a0f8f77afb?ref=ghost.org" rel="noopener noreferrer"&gt;Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@TryGhost?ref=ghost.org" rel="noopener noreferrer"&gt;Videos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.notion.so/Ghost-Starter-Theme-update-3ca13b3e81994e97beb7e5497bdb67e5?ref=ghost.org" rel="noopener noreferrer"&gt;The official Ghost Forum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ghost.org/tutorials/#/portal/signup/free" rel="noopener noreferrer"&gt;Build with Ghost Newsletter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ghost</category>
      <category>beginners</category>
      <category>node</category>
    </item>
    <item>
      <title>How to debug your Ghost theme</title>
      <dc:creator>Ryan Feigenbaum</dc:creator>
      <pubDate>Thu, 16 Feb 2023 20:21:10 +0000</pubDate>
      <link>https://forem.com/tryghost/how-to-debug-your-ghost-theme-53ij</link>
      <guid>https://forem.com/tryghost/how-to-debug-your-ghost-theme-53ij</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/c6t99ODwGIQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Like a behind-the-scenes look at your favorite film, Ghost’s &lt;code&gt;{{log}}&lt;/code&gt; helper lets you see the data that’s behind your theme, empowering you to debug your theme like a pro. In this tutorial, we’ll follow Jamie, as they work on their new Ghost site, &lt;em&gt;Cats in Art&lt;/em&gt;. Using real-world examples, we'll show you everything you need to know about the log helper, from getting started to traversing contexts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ghost.org/docs/themes/helpers/log/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdxq6865lq35xdhhna3s.png" alt="Ghost log helper" width="486" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Ghost in development mode
&lt;/h2&gt;

&lt;p&gt;To use the &lt;code&gt;{{log}}&lt;/code&gt; helper, start Ghost in development mode. In a terminal, navigate to the directory where Ghost is &lt;a href="https://ghost.org/docs/install/local/" rel="noopener noreferrer"&gt;locally installed&lt;/a&gt; and run &lt;code&gt;ghost run -D&lt;/code&gt;. This command tells Ghost to start and output logging information to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ghost run &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a browser and navigate to your local Ghost site. For every page load, requests for the page and its assets (like images) now appear in the console. Refresh the page to see the data logged in real-time. (Hit &lt;code&gt;ctrl + c&lt;/code&gt; in the terminal to stop Ghost’s development mode.)&lt;/p&gt;

&lt;p&gt;Open Ghost Admin, navigate to &lt;strong&gt;Settings&lt;/strong&gt;, and set your theme. You’re now ready to start using the &lt;code&gt;{{log}}&lt;/code&gt; helper 🏃‍♀️&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To see real-time changes, be sure your theme folder is located in (or linked to) Ghost’s &lt;code&gt;content/themes&lt;/code&gt; folder.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the &lt;code&gt;{{log}}&lt;/code&gt; helper
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;{{log}}&lt;/code&gt; helper shows which data is available at a particular place in your theme. &lt;em&gt;Cats in Art&lt;/em&gt; is a site — you guessed it — featuring cats in art. It uses Ghost’s default theme, &lt;a href="https://demo.ghost.io" rel="noopener noreferrer"&gt;Casper&lt;/a&gt;.  As we follow Jamie’s use of the &lt;code&gt;{{log}}&lt;/code&gt; helper, know that the same techniques shown below work with all Ghost themes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log specific data
&lt;/h2&gt;

&lt;p&gt;Jamie added a sweet gradient to use as the background image to their author profile, but the image isn’t appearing on the site. They open the &lt;code&gt;author.hbs&lt;/code&gt; file, which Casper uses to render author profile pages, and find the &lt;a href="https://github.com/TryGhost/Casper/blob/a007415d8909bf0acf8fdab20747611e1f0cb333/author.hbs#L16" rel="noopener noreferrer"&gt;code for the background image&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-image"&lt;/span&gt;
        &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"s"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 300w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"m"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 600w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"l"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 1000w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"xl"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 2000w"&lt;/span&gt;
        &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1000px) 400px, 800px"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"m"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjtg2uag0k2ys1jqejbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjtg2uag0k2ys1jqejbn.png" alt="Image not found" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By running in development mode, Jamie already has a vague clue about what’s causing the bug. There’s a 404 error that tells them the image file isn’t being found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qrsf1vrpb4z95gw6ho7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qrsf1vrpb4z95gw6ho7.png" alt="image not found, 404" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the log helper by combining the log keyword with the property to be logged:  &lt;code&gt;{{log property_to_logged}}&lt;/code&gt;. To help investigate their missing image, Jamie log’s the value of &lt;code&gt;feature_image&lt;/code&gt; by adding the log helper to the template and refreshing the page in the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-image"&lt;/span&gt;
        &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"s"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 300w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"m"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 600w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"l"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 1000w,
                &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"xl"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 2000w"&lt;/span&gt;
        &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1000px) 400px, 800px"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;img_url&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"m"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf8282h030mnsj8aqnn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf8282h030mnsj8aqnn4.png" alt="terminal shows undefined" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; now appears in the log. From here, Jamie guesses that they're likely not using the right image property, which is why the image is not coming through. To be sure, Jamie annotates the log helper with static text. This technique is particularly useful when using multiple log helpers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"feature image:"&lt;/span&gt; &lt;span class="nv"&gt;feature_image&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff234bm35znh06gqpqrzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff234bm35znh06gqpqrzs.png" alt="Image is undefined" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding this bit of text confirms that the feature_image property is undefined — Jamie’s bummed the property value is wrong but relieved to know what the bug is 😅&lt;/p&gt;

&lt;h3&gt;
  
  
  Log all available data
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;feature_image&lt;/code&gt; isn’t the right property, then what is? Jamie uses &lt;code&gt;{{log this}}&lt;/code&gt; to show all available data in a context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="bp"&gt;this&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of &lt;code&gt;log this&lt;/code&gt; as saying “log &lt;em&gt;this&lt;/em&gt; data right here.” Jamie now gets this output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpc3nvq0eb65uf95i53o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpc3nvq0eb65uf95i53o.png" alt="author data" width="800" height="481"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jamie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jamie Larson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;profile_image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;cover_image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:2368/content/images/2023/01/mesh-gradient.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;facebook&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@jamie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;meta_title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;meta_description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:2368/author/jamie/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of the properties listed here — &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;slug&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;bio&lt;/code&gt;, etc. — can be used in the theme template. For example, &lt;code&gt;{{name}}&lt;/code&gt; renders as “Jamie Larson” on the live site.&lt;/p&gt;

&lt;p&gt;Looking through the available data, the error becomes clear. &lt;code&gt;feature_image&lt;/code&gt; is not the right property. It’s actually &lt;code&gt;cover_image&lt;/code&gt;. Jamie can tell that’s the property they need because of its name (cover image) and because it’s the only one with an image URL.&lt;/p&gt;

&lt;p&gt;Changing the property to &lt;code&gt;cover_image&lt;/code&gt; returns Jamie’s author page with their sweet gradient image loading as expected!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88lzsr4598zh1tilwe0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88lzsr4598zh1tilwe0y.png" alt="sweet gradient now shows" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You might be thinking that Jamie could’ve figured out their bug much quicker by just reading the docs for the author's context. That’s probably true, but Jamie’s mischievous cat had chewed through the ethernet cable, leaving Jamie without internet and without access to the docs. &lt;code&gt;{{log}}&lt;/code&gt; was their saving grace.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing more with the log helper
&lt;/h2&gt;

&lt;p&gt;Now that Jamie has a good handle on how the &lt;code&gt;log&lt;/code&gt; helper works, they take it for a spin to see what else it can do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging post data
&lt;/h3&gt;

&lt;p&gt;Jamie opens their post template (&lt;code&gt;post.hbs&lt;/code&gt;). Inside the post context, they add &lt;code&gt;{{log this}}&lt;/code&gt; and save the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;post&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
...
&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="bp"&gt;this&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
...
&lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Loading a post outputs lots of data to the terminal, probably too much to fit on the screen all at once. This data represents everything available to use in the theme when inside the post context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8l1ospqugaqrkxv6au30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8l1ospqugaqrkxv6au30.png" alt="logging post data to terminal" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jamie sees familiar properties like &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;feature_image&lt;/code&gt; and a whole lot more.&lt;/p&gt;

&lt;p&gt;But Jamie's really only interested in seeing the tags for the post. They update the log helper with the property name and annotate it to make it easier to recognize in the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"tags:"&lt;/span&gt; &lt;span class="nv"&gt;tags&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiid06hfdl1g6xi2l1yir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiid06hfdl1g6xi2l1yir.png" alt="Tags logged to the console" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, Jamie sees that this post has one tag associated with it, “Print.” (On &lt;em&gt;Cats in Art&lt;/em&gt;, each artwork is tagged with which kind of art it is.) They also see all the available tag data, which is editable on the tag page in Ghost Admin.&lt;/p&gt;

&lt;p&gt;Jamie notices that each tag has an &lt;code&gt;accent_color&lt;/code&gt; property, which gives them the idea of color-coding tags in a future design 🎨&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching contexts
&lt;/h3&gt;

&lt;p&gt;Jamie’s feeling pretty good now. There’s nothing they can’t log 💪&lt;/p&gt;

&lt;p&gt;But just as they’re ready to sign off for the day, Jamie thinks it would be cool to include the number of posts they’ve written about cats in art on their author page.&lt;/p&gt;

&lt;p&gt;They log author data only to see that publication count isn’t included. Jamie thinks about where this data might be available.&lt;/p&gt;

&lt;p&gt;The author template (&lt;code&gt;author.hbs&lt;/code&gt;) renders a collection of posts — everything an author’s published. In Ghost, this collection is paginated by default. The pagination object includes lots of useful data like the current page number, the number of posts per page, and the total number of posts available. This &lt;code&gt;total&lt;/code&gt; property is exactly what Jamie needs: the total number of posts they’ve published about cats in art.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ghost.org/docs/themes/helpers/pagination/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlq57pni4qdgnmn96119.png" alt="Pagination helper" width="486" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jamie updates the author template to include this data, wrapped in a &lt;code&gt;span&lt;/code&gt; to make it easier to style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"author-post-count"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They refresh the page, hoping to see their post count, but it doesn’t work. Jamie then tries logging it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"post count:"&lt;/span&gt; &lt;span class="nv"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpglvshkcmwumm1qwjpg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpglvshkcmwumm1qwjpg9.png" alt="Terminal shows post count undefined" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Undefined!? Jamie’s now been at this awhile, racking their brain for why this isn’t working, and just about ready to hurl their laptop out the window into the evening breeze. But instead, they pet their mischievous cat and get back to it.&lt;/p&gt;

&lt;p&gt;Jamie knows they’re currently in the &lt;code&gt;#author&lt;/code&gt; context, but pagination data is only available in the top-level context. That means Jamie needs to exit the current context and go up a level to get the post count, like if you were on the main floor but what you needed to get was upstairs.&lt;/p&gt;

&lt;p&gt;Jamie goes up a level by prefixing the property with &lt;code&gt;../&lt;/code&gt;, similar to how you navigate the file system in the terminal. The &lt;code&gt;log&lt;/code&gt; helper now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"post count:"&lt;/span&gt; &lt;span class="nv"&gt;..&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9nwyurov4742psjqj61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9nwyurov4742psjqj61.png" alt="Post count correctly logged to the terminal" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, the helper now logs the post count as 100. That’s promising. Jamie updates their template with this new syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-card-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"author-post-count"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;..&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf8o6jrs8jk428xwgsq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf8o6jrs8jk428xwgsq5.png" alt="post count showing correctly" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aw, yeah. Looking great! Post count now displays correctly, and Jamie contemplates whether to write post 101 or to call it a night, for real this time.&lt;/p&gt;

&lt;p&gt;Whatever Jamie decides, changing context is an indispensable tool when developing Ghost themes, and using it with the &lt;code&gt;log&lt;/code&gt; helper gives the developer the ability to see which data is available, no matter where they are in the theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you followed Jamie in their use of the &lt;code&gt;log&lt;/code&gt; helper to output specific data, all available data by using &lt;code&gt;this&lt;/code&gt;, and data from different contexts.&lt;/p&gt;

&lt;p&gt;Knowing how to use the &lt;code&gt;log&lt;/code&gt; helper unlocks a whole new perspective for custom theme development. You now have direct access to the data your templates are rendering. This new superpower will boost your ability to squash bugs and amplify your creative possibilities 🌈&lt;/p&gt;

&lt;p&gt;Let us know what you find, what you create, and whether you’re getting stuck &lt;a href="https://forum.ghost.org" rel="noopener noreferrer"&gt;on our official Forum&lt;/a&gt;, where we talk about all things Ghost.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>startup</category>
      <category>development</category>
      <category>career</category>
    </item>
    <item>
      <title>Create a Google News sitemap with Ghost</title>
      <dc:creator>Ryan Feigenbaum</dc:creator>
      <pubDate>Tue, 25 Oct 2022 20:38:37 +0000</pubDate>
      <link>https://forem.com/tryghost/create-a-google-news-sitemap-with-ghost-3n25</link>
      <guid>https://forem.com/tryghost/create-a-google-news-sitemap-with-ghost-3n25</guid>
      <description>&lt;p&gt;Add your content to Google News by adding a &lt;a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap" rel="noopener noreferrer"&gt;Google News sitemap&lt;/a&gt; to your Ghost site. The sitemap lets Google News know all the important details about your published content.&lt;/p&gt;

&lt;p&gt;This tutorial walks you through how to create a Google News sitemap by implementing a new route on your Ghost site and by creating a custom template that is fully optimized for the Google News aggregator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a new route for your sitemap
&lt;/h2&gt;

&lt;p&gt;To make your sitemap accessible on the web, add a new route using Ghost's dynamic routing layer.&lt;/p&gt;

&lt;p&gt;Download the most up-to-date version of your &lt;code&gt;routes.yaml&lt;/code&gt; file from Ghost Admin. Go to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Labs&lt;/strong&gt; → &lt;strong&gt;Download current routes.yaml&lt;/strong&gt;. Open the file in your &lt;a href="https://dev.to/royalfig/open-a-theme-in-a-code-editor-5fb0-temp-slug-4104622"&gt;code editor&lt;/a&gt; of choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffduvt7yol5zv6f6v20y3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffduvt7yol5zv6f6v20y3.png" alt="Create a Google News sitemap" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the following route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/sitemap/&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sitemap&lt;/span&gt;
    &lt;span class="na"&gt;content_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This entry tells Ghost to create a route at &lt;code&gt;yoursite.com/sitemap/&lt;/code&gt;, and on that route, to serve the &lt;code&gt;sitemap&lt;/code&gt; template file and send an XML response (which is what Google News expects).&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;routes.yaml&lt;/code&gt; file should now look like the file below, plus any other changes you may have made to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/sitemap/&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sitemap&lt;/span&gt;
    &lt;span class="na"&gt;content_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/xml&lt;/span&gt;

&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;permalink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/{slug}/&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index&lt;/span&gt;

&lt;span class="na"&gt;taxonomies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tag/{slug}/&lt;/span&gt;
  &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/author/{slug}/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and upload it to Ghost Admin ( &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Labs&lt;/strong&gt; → &lt;strong&gt;Upload routes YAML&lt;/strong&gt; ).&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a template for the Google News sitemap
&lt;/h2&gt;

&lt;p&gt;The next step is to create a Handlebars template in your theme. This requires a little bit of coding, but the example in this tutorial provides a fully functional starting point.&lt;/p&gt;

&lt;p&gt;Create a new file in the root of your theme called &lt;code&gt;sitemap.hbs&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add your posts
&lt;/h3&gt;

&lt;p&gt;The code below tells Ghost to fetch your latest 1,000 posts and format the information for Google News. The template follows &lt;a href="https://www.google.com/schemas/sitemap-news/0.9/sitemap-news.xsd" rel="noopener noreferrer"&gt;the specifications for a Google News sitemap&lt;/a&gt; like including the title and publication date as well as indicating if the content is for members only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;urlset&lt;/span&gt;
    &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.sitemaps.org/schemas/sitemap/0.9"&lt;/span&gt;
    &lt;span class="na"&gt;xmlns:news=&lt;/span&gt;&lt;span class="s"&gt;"http://www.google.com/schemas/sitemap-news/0.9"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="nv"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1000"&lt;/span&gt; &lt;span class="nv"&gt;include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tags"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
      &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;foreach&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="nv"&gt;absolute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;news:news&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:publication&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;news:name&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/news:name&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;news:language&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;locale&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/news:language&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/news:publication&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;has&lt;/span&gt; &lt;span class="nv"&gt;visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"members"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:access&amp;gt;&lt;/span&gt;Registration&lt;span class="nt"&gt;&amp;lt;/news:access&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;has&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
            &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;has&lt;/span&gt; &lt;span class="nv"&gt;visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"paid"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:access&amp;gt;&lt;/span&gt;Subscription&lt;span class="nt"&gt;&amp;lt;/news:access&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;has&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:publication_date&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;date&lt;/span&gt; &lt;span class="nv"&gt;published_at&lt;/span&gt; &lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-DDTHH:mm:ssZ"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/news:publication_date&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:title&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/news:title&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;news:keywords&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;tags&lt;/span&gt; &lt;span class="nv"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"5"&lt;/span&gt; &lt;span class="nv"&gt;autolink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/news:keywords&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/news:news&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;foreach&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;get&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/urlset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy and paste this code to the &lt;code&gt;sitemap.hbs&lt;/code&gt; file or customize it to suit your needs. With customization, be aware that Google is stringent about attributes and formatting, so be sure &lt;a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap#news-specific-tag-definitions" rel="noopener noreferrer"&gt;to include all required fields&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update your theme
&lt;/h2&gt;

&lt;p&gt;Save the new template, zip up your theme, and &lt;a href="https://dev.to/royalfig/download-and-upload-a-theme-1f2b-temp-slug-9378121"&gt;upload it to your Ghost site&lt;/a&gt;. Check to see if your Google News sitemap is active by visiting &lt;code&gt;yoursite.com/sitemap/&lt;/code&gt;. (We added a sitemap to this Tutorials site as an example: &lt;a href="https://ghost.org/tutorials/sitemap/" rel="noopener noreferrer"&gt;https://ghost.org/tutorials/sitemap/&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszr0uho48akl0qraetc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszr0uho48akl0qraetc9.png" alt="Create a Google News sitemap" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit your Google News sitemap
&lt;/h2&gt;

&lt;p&gt;Google Search Console is essential for monitoring your search performance in Google. &lt;a href="https://ghost.org/integrations/google-search-console/" rel="noopener noreferrer"&gt;See our guide on integrating it with Ghost&lt;/a&gt;. It's also the best way to let Google know about your new Google News sitemap.&lt;/p&gt;

&lt;p&gt;In Google Search Console, go to Sitemaps and add your new URL to the &lt;strong&gt;Add a new sitemap&lt;/strong&gt; box. Click &lt;strong&gt;Submit&lt;/strong&gt;. The sitemap should be fetched immediately, but occasionally it can take some time for Google to crawl the URLs listed in it. A successful status indicates that Google has indexed all of your links submitted to Google News. That means your content is available to show up on Google News 📰&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you used Ghost's routing system and theme layer to create a custom Google News sitemap 🙌&lt;/p&gt;

&lt;p&gt;Creating the sitemap helps your content show up in Google News, but Google also provides several other options for customization, monetization, and improved placement. See &lt;a href="https://support.google.com/news/publisher-center/answer/9607025?hl=en" rel="noopener noreferrer"&gt;Google's guide&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Having trouble with this tutorial or seeing great success with your new Google News sitemap? Come share your experience in the &lt;a href="https://forum.ghost.org/" rel="noopener noreferrer"&gt;official Ghost Forum&lt;/a&gt;, where developers, publishers, and content creators come together to discuss all things Ghost.&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>handlebars</category>
    </item>
    <item>
      <title>Hacktoberfest with Ghost: Help bring i18n to our app</title>
      <dc:creator>Ryan Feigenbaum</dc:creator>
      <pubDate>Mon, 17 Oct 2022 14:40:05 +0000</pubDate>
      <link>https://forem.com/tryghost/hacktoberfest-with-ghost-help-bring-i18n-to-our-app-1a1m</link>
      <guid>https://forem.com/tryghost/hacktoberfest-with-ghost-help-bring-i18n-to-our-app-1a1m</guid>
      <description>&lt;p&gt;&lt;a href="https://ghost.org/" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt; is an industry-leading, open-source publishing platform built on Node. Creators (Ali Abdaal, Scatchy, DESK), publishers (The Lever, The Atlantic, The Browser), and companies (Unsplash, DuckDuckGo, OpenAI, Mozilla) use our platform to publish, share, and grow a business around their content.&lt;/p&gt;

&lt;p&gt;Because our users are global, &lt;strong&gt;better translation support (i18n)&lt;/strong&gt; has become our most-requested feature. Hacktoberfest seemed like the perfect opportunity to ask the dev community for help in shipping this much-wanted feature.&lt;/p&gt;

&lt;p&gt;While developers have contributed to translating individual components in the past, we believe that i18n needs to be solved with a single solution. Therefore, we put together a plan that outlines the requirements for this solution.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        i18n Milestone 2: Framework and build tools
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#15502&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/ErisDS" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F101513%3Fv%3D4" alt="ErisDS avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/ErisDS" rel="noopener noreferrer"&gt;ErisDS&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 29, 2022&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;em&gt;This issue represents a single milestone within our i18n project. The full project is documented &lt;a href="https://forum.ghost.org/t/hacktoberfest-project-i18n-for-portal-member-emails-comments-ui-sodosearch/33062" rel="nofollow noopener noreferrer"&gt;on the Ghost forum&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected structure&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;ghost/locales&lt;/code&gt; folder in the monorepo contains namespaced JSON files e.g. &lt;code&gt;locales/es/common.json&lt;/code&gt;, &lt;code&gt;/ocales/es/portal.json&lt;/code&gt;, &lt;code&gt;locales/es/email.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For Node.js/email-templates we do one of:
&lt;ul&gt;
&lt;li&gt;(Preferred) The correct path is loaded from config based on environment&lt;/li&gt;
&lt;li&gt;ghost/locales gets copied into ghost/core somehow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then for react:
&lt;ul&gt;
&lt;li&gt;Use code-splitting to generate individual versions of portal for each supported locale.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Goals:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Get the basic pieces of i18next in place &amp;amp; configured according to &lt;a href="https://forum.ghost.org/t/hacktoberfest-project-i18n-for-portal-member-emails-comments-ui-sodosearch/33062#technical-plan-3" rel="nofollow noopener noreferrer"&gt;the tech spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Implement the expected structure (see above)&lt;/li&gt;
&lt;li&gt;[ ] Figure out if we need to switch to vitejs in Portal to achieve the desired build (may need help from Ghost team)&lt;/li&gt;
&lt;li&gt;[ ] Have 1 interpolated string in each of email and Portal&lt;/li&gt;
&lt;li&gt;[ ] Tooling for extracting strings &amp;amp; generating files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Tasks:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Move Portal into the monorepo (Ghost team to complete soon)&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of the build tool for Portal - I think this is the hardest part so we should do it first&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of how locale files get pulled into Node.js for email templates&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of i18next wiring so that there's one interpolated string in Portal and one in an email template&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of a tooling that can grab the 2 interpolated strings e.g. &lt;code&gt;yarn translate es&lt;/code&gt; would create &lt;code&gt;/locales/es/portal.js&lt;/code&gt; and &lt;code&gt;/locales/es/email.js&lt;/code&gt; each containing one string.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm hoping to find people interested in the different parts to do initial spike implementations - then we can figure out the tasks more clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes on participating &amp;amp; Hacktoberfest:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We aren't going to merge half-baked spikes or wiring that doesn't match the spec yet - so if you're just trying to get through Hacktoberfest as fast as possible, this is not the issue for you! (There are plenty of small bugs though!)&lt;/p&gt;
&lt;p&gt;However, if you're using Hacktoberfest as a vehicle to find a cool &amp;amp; meaningful project to work on, this should be well up your street :)&lt;/p&gt;
&lt;p&gt;We'll do our best to ensure that those who actively contribute value to this project get something merged and appropriately labelled by the end of Hacktoberfest 🎃&lt;/p&gt;
&lt;p&gt;👉 If you're interested, drop a note to say Hi and which part you're interested in before getting stuck in, just in case we end up with multiple people trying to get the same things merged.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;At present, only our theme layer is translatable using a custom-built i18n framework (that we do not want to build on). Our native membership (Portal), email, comment, and search systems, however, are not translatable.&lt;/p&gt;

&lt;p&gt;Since these systems only have one set of strings that need translating into each language, it makes sense to have these translations be part of Ghost core (that is, built into the product itself).&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;We want to be able to serve translated emails and widgets (membership, comments, search), based on the language defined by the user in general settings. Technically, the solution boils down to answering two key questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where do the translated strings come from?&lt;/li&gt;
&lt;li&gt;How do the replacement strings get interpolated?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answer to (1) is “a JSON file” of some description. We do not want to manage translations in a 3rd party tool like transifex, but we’d like to keep all of the JSON files in GitHub. It needs to be possible to load the strings and pass them to all the places they’re needed.&lt;/p&gt;

&lt;p&gt;The answer to (2) is a &lt;code&gt;{{t}}&lt;/code&gt; helper that needs to exist in both React and email templates. We want to keep our existing pattern in themes of using the default string as the key. This means that when a default string is changed, it needs to be re-translated in all languages, which is desirable as changes are usually more than cosmetic.&lt;/p&gt;

&lt;p&gt;We will also need a way to collect all the default strings and compile them into a new JSON file, ready to be translated.&lt;/p&gt;

&lt;p&gt;For email templates, it makes sense for the interpolation to be dynamic, done at the time of sending as this is a server-side system. For our React widgets, we would like to create a custom build for each translation, so it becomes possible to load language-specific scripts, e.g. &lt;code&gt;portal-es.min.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Several aspects of i18n will be made significantly easier by ensuring that all translatable components and the translations themselves live together in the Ghost monorepo. This was the first part of the process, which is already complete 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical plan
&lt;/h2&gt;

&lt;p&gt;Our basic requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load a single set of translations + (maybe) a set of custom strings into React widgets&lt;/li&gt;
&lt;li&gt;Load a single set of translations into Ghost core for emails&lt;/li&gt;
&lt;li&gt;Future: Load translations for Admin&lt;/li&gt;
&lt;li&gt;Interpolation tools for Handlebars and React&lt;/li&gt;
&lt;li&gt;Be able to use the default string as the key&lt;/li&gt;
&lt;li&gt;Be able to generate a new language file with all available keys&lt;/li&gt;
&lt;li&gt;Not have to load all translations, e.g., all of Portal’s translations into comments&lt;/li&gt;
&lt;li&gt;Have a single place, in GitHub, where locales are defined&lt;/li&gt;
&lt;li&gt;Ideally: generate a build per locale for each JS widget, e.g., &lt;code&gt;portal-es.min.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last requirement is the hardest, as we will need to figure out a build step for our react projects… but this should be a one-off piece.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;i18next 4&lt;/a&gt; framework appears to support every single one of our requirements, including &lt;a href="https://www.i18next.com/principles/fallback" rel="noopener noreferrer"&gt;fallback-based keys&lt;/a&gt;, &lt;a href="https://www.i18next.com/principles/namespaces" rel="noopener noreferrer"&gt;namespacing&lt;/a&gt;, and having tools for &lt;a href="https://www.i18next.com/how-to/extracting-translations" rel="noopener noreferrer"&gt;extracting translation strings&lt;/a&gt;. It’s also extensible with plugins and has links to tooling for managing the translation process—so we could have a &lt;code&gt;yarn check-translations&lt;/code&gt; command to view what’s not translated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected structure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;ghost/locales&lt;/code&gt; folder in the monorepo that contains namespaced JSON files, e.g., &lt;code&gt;locales/es/common.json&lt;/code&gt;, &lt;code&gt;locales/es/portal.json&lt;/code&gt; , &lt;code&gt;locales/es/email.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For Node.js/email-templates we do one of:

&lt;ul&gt;
&lt;li&gt;(Preferred) The correct path is loaded from config based on environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ghost/locales&lt;/code&gt; gets copied into &lt;code&gt;ghost/core&lt;/code&gt; somehow&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Then for React:

&lt;ul&gt;
&lt;li&gt;Use code-splitting to generate individual versions of portal, comments, and search for each supported locale.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here are &lt;a href="https://levelup.gitconnected.com/code-splitting-react-i18n-locales-using-dynamic-imports-97af247ad6f0" rel="noopener noreferrer"&gt;some details&lt;/a&gt; of using i18next with Webpack to create a a build step using “code splitting,” but it doesn’t quite achieve what we want. We are currently investigating switching to Vite for our build tool—and this may end up being an additional prerequisite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Milestones
&lt;/h2&gt;

&lt;p&gt;This project has prerequisites, is fairly complex, and has lots of different phases in which different community members may wish to get involved. Therefore, we’ve broken the project down into milestones and will keep this thread up to date with where we are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Milestone 1: Monorepo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Portal needs to be merged into the monorepo ✅&lt;/li&gt;
&lt;li&gt;Comments and search will follow later, but the project can continue without them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who: This milestone has been completed by the Ghost team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Milestone 2: Framework and build tools&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the “expected structure” from above&lt;/li&gt;
&lt;li&gt;Get the basic pieces of i18next in place&lt;/li&gt;
&lt;li&gt;Figure out if we need to switch to Vite in Portal (may need help from Ghost team)&lt;/li&gt;
&lt;li&gt;Have one interpolated string in email and Portal&lt;/li&gt;
&lt;li&gt;Tools for extracting strings &amp;amp; generating files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who: Looking for interested contributors, Ghost team will help and/or kick this off if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Milestone 3: Interpolated Strings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All strings in Portal and Member emails (signup, signin, subscribe) are interpolated using the &lt;code&gt;{{t}}&lt;/code&gt; helper and the default, existing US English string as the key.&lt;/li&gt;
&lt;li&gt;comments-ui and SodoSearch should also be in the monorepo by now, and so these strings can also be interpolated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who: Looking for interested contributors&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Milestone 4: Translations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tooling for extracting strings into new locale files should be perfected&lt;/li&gt;
&lt;li&gt;Getting all strings translated for additional languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who: Looking for interested contributors&lt;/p&gt;




&lt;p&gt;Check out the issue on GitHub for more info:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        i18n Milestone 2: Framework and build tools
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#15502&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/ErisDS" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F101513%3Fv%3D4" alt="ErisDS avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/ErisDS" rel="noopener noreferrer"&gt;ErisDS&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 29, 2022&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;em&gt;This issue represents a single milestone within our i18n project. The full project is documented &lt;a href="https://forum.ghost.org/t/hacktoberfest-project-i18n-for-portal-member-emails-comments-ui-sodosearch/33062" rel="nofollow noopener noreferrer"&gt;on the Ghost forum&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected structure&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;ghost/locales&lt;/code&gt; folder in the monorepo contains namespaced JSON files e.g. &lt;code&gt;locales/es/common.json&lt;/code&gt;, &lt;code&gt;/ocales/es/portal.json&lt;/code&gt;, &lt;code&gt;locales/es/email.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For Node.js/email-templates we do one of:
&lt;ul&gt;
&lt;li&gt;(Preferred) The correct path is loaded from config based on environment&lt;/li&gt;
&lt;li&gt;ghost/locales gets copied into ghost/core somehow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then for react:
&lt;ul&gt;
&lt;li&gt;Use code-splitting to generate individual versions of portal for each supported locale.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Goals:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Get the basic pieces of i18next in place &amp;amp; configured according to &lt;a href="https://forum.ghost.org/t/hacktoberfest-project-i18n-for-portal-member-emails-comments-ui-sodosearch/33062#technical-plan-3" rel="nofollow noopener noreferrer"&gt;the tech spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Implement the expected structure (see above)&lt;/li&gt;
&lt;li&gt;[ ] Figure out if we need to switch to vitejs in Portal to achieve the desired build (may need help from Ghost team)&lt;/li&gt;
&lt;li&gt;[ ] Have 1 interpolated string in each of email and Portal&lt;/li&gt;
&lt;li&gt;[ ] Tooling for extracting strings &amp;amp; generating files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Tasks:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Move Portal into the monorepo (Ghost team to complete soon)&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of the build tool for Portal - I think this is the hardest part so we should do it first&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of how locale files get pulled into Node.js for email templates&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of i18next wiring so that there's one interpolated string in Portal and one in an email template&lt;/li&gt;
&lt;li&gt;[ ] Demo/spike of a tooling that can grab the 2 interpolated strings e.g. &lt;code&gt;yarn translate es&lt;/code&gt; would create &lt;code&gt;/locales/es/portal.js&lt;/code&gt; and &lt;code&gt;/locales/es/email.js&lt;/code&gt; each containing one string.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm hoping to find people interested in the different parts to do initial spike implementations - then we can figure out the tasks more clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes on participating &amp;amp; Hacktoberfest:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We aren't going to merge half-baked spikes or wiring that doesn't match the spec yet - so if you're just trying to get through Hacktoberfest as fast as possible, this is not the issue for you! (There are plenty of small bugs though!)&lt;/p&gt;
&lt;p&gt;However, if you're using Hacktoberfest as a vehicle to find a cool &amp;amp; meaningful project to work on, this should be well up your street :)&lt;/p&gt;
&lt;p&gt;We'll do our best to ensure that those who actively contribute value to this project get something merged and appropriately labelled by the end of Hacktoberfest 🎃&lt;/p&gt;
&lt;p&gt;👉 If you're interested, drop a note to say Hi and which part you're interested in before getting stuck in, just in case we end up with multiple people trying to get the same things merged.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TryGhost/Ghost/issues/15502" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Find more info in &lt;a href="https://forum.ghost.org/t/portal-and-email-component-translations/19144/29" rel="noopener noreferrer"&gt;our Forum&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in helping with milestones 2-4 of this project, we’d love to hear from you in the comments below 👇&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>node</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to add a tip jar to a Ghost site</title>
      <dc:creator>Ryan Feigenbaum</dc:creator>
      <pubDate>Fri, 22 Jul 2022 19:21:24 +0000</pubDate>
      <link>https://forem.com/tryghost/how-to-add-a-tip-jar-to-a-ghost-site-3nm3</link>
      <guid>https://forem.com/tryghost/how-to-add-a-tip-jar-to-a-ghost-site-3nm3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jy2b8ktvhqbyx6v3wgy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jy2b8ktvhqbyx6v3wgy.jpg" alt="How to add a tip jar" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Offer your most engaged readers multiple ways to support your content by adding a tip jar to your Ghost membership site with Stripe.&lt;/p&gt;

&lt;p&gt;This tutorial will walk you through the steps of setting up the payment link in Stripe and how to add the link to your Ghost site as a navigation item, a page, and a content snippet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a payment link on Stripe
&lt;/h2&gt;

&lt;p&gt;Log in to your stripe account and go to &lt;strong&gt;Payments → Payment links&lt;/strong&gt;. Click &lt;strong&gt;New&lt;/strong&gt; to create a new payment link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feg0scp8efujlopdljc6o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feg0scp8efujlopdljc6o.jpg" alt="How to add a tip jar" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;Create payment link&lt;/strong&gt; page, select &lt;strong&gt;Let customers choose what to pay&lt;/strong&gt; from the dropdown menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cpee17snf8fdu51qrhi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cpee17snf8fdu51qrhi.jpg" alt="How to add a tip jar" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the details for your payment link. These details are entirely up to you. When everything looks good, click &lt;strong&gt;Create link&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqf4fyujsb80dw6vuxcg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqf4fyujsb80dw6vuxcg.jpg" alt="How to add a tip jar" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your payment link is now created. Click &lt;strong&gt;Copy&lt;/strong&gt; to get the URL that we’ll use with Ghost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza48uki1gx92gksb7eld.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza48uki1gx92gksb7eld.jpg" alt="How to add a tip jar" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a visitor clicks the link, they’ll be taken to the Stripe checkout page, where they can enter their details and complete the payment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4qanqg8cogxq9s2f8wb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4qanqg8cogxq9s2f8wb.jpg" alt="How to add a tip jar" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the payment link to Ghost
&lt;/h2&gt;

&lt;p&gt;Anywhere you can put a URL, you can use your payment link — we’re going to focus on adding it as a navigation item, a new page, and a snippet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigation item
&lt;/h3&gt;

&lt;p&gt;If you’ve ever added a navigation item to your Ghost site, then you’re already ahead of the curve on this one, because the process with a payment link is exactly the same.&lt;/p&gt;

&lt;p&gt;Log into your Ghost site and go to &lt;strong&gt;Navigation&lt;/strong&gt;. Add the link text and payment link URL. Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzu807pxhd3dyenv8rfku.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzu807pxhd3dyenv8rfku.jpg" alt="How to add a tip jar" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it! Your tip jar is now live and active 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxn9j1h1ac3gzkep095a4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxn9j1h1ac3gzkep095a4.jpg" alt="How to add a tip jar" width="800" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Page
&lt;/h3&gt;

&lt;p&gt;Maybe you want to make more of a case for a tip than just a navigation item. Another option to showcase your payment link is to create a new page in Ghost.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Pages → New page&lt;/strong&gt;. The design and content of the page are up to you. In the example below, we created a fictional publication, &lt;em&gt;Coffee Talk&lt;/em&gt;. The page includes some copy along with a &lt;a href="https://ghost.org/changelog/buttons/" rel="noopener noreferrer"&gt;&lt;code&gt;Button&lt;/code&gt; card&lt;/a&gt; that uses the Stripe payment link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdze80x4r7k38mu0azkgh.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdze80x4r7k38mu0azkgh.jpeg" alt="How to add a tip jar" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Publish the page and add it to the navigation bar, like in the previous section, or share it in newsletters, on social media, or wherever there’s an opportunity for support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a content snippet
&lt;/h3&gt;

&lt;p&gt;In Ghost, &lt;a href="https://ghost.org/changelog/content-snippets/" rel="noopener noreferrer"&gt;snippets&lt;/a&gt; are user-defined reusable bits of content. With them, you can easily copy and paste content across different posts and pages.&lt;/p&gt;

&lt;p&gt;Here, they’re perfect for quickly adding a tip jar at opportune moments in your posts.&lt;/p&gt;

&lt;p&gt;Using our &lt;em&gt;Coffee Talk&lt;/em&gt; publication from above, let’s say we just shared a technique for brewing the most insane cup of coffee ever known. Now’s as good a time as any to make a call for action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2uquql4du0m6aduldk1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2uquql4du0m6aduldk1.jpeg" alt="How to add a tip jar" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this CTA, we used the &lt;a href="https://ghost.org/changelog/headers/" rel="noopener noreferrer"&gt;Header card&lt;/a&gt;, which adds a beautiful, full-width element to the page with a customizable button (where we included the Stripe payment link).&lt;/p&gt;

&lt;p&gt;To be able to use this on other posts and pages, we can turn the card into a snippet (which works for any post content — not just Header cards).&lt;/p&gt;

&lt;p&gt;To create a snippet, select content and click the create snippet icon. Enter a name for your snippet and hit &lt;strong&gt;Save&lt;/strong&gt;. Your snippet is now available on any post or page from the card menu 🤯&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Tip jars offer your visitors a simple way to contribute to your content financially with a one-off payment, and thanks to Stripe and Ghost, this is easy to set up for your publication&lt;/p&gt;

&lt;p&gt;How did you implement a tip jar on your website? &lt;a href="https://forum.ghost.org" rel="noopener noreferrer"&gt;Join the Forum&lt;/a&gt; to share your witty copy, ask questions, or just see what other Ghost users are up to.&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>stripe</category>
      <category>creator</category>
    </item>
  </channel>
</rss>
