<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maciek Palmowski</title>
    <description>The latest articles on DEV Community by Maciek Palmowski (@palmiak).</description>
    <link>https://dev.to/palmiak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F245127%2F2376b76f-78f6-49c0-b787-2c97b9f2ed5f.jpeg</url>
      <title>DEV Community: Maciek Palmowski</title>
      <link>https://dev.to/palmiak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/palmiak"/>
    <language>en</language>
    <item>
      <title>How to easily start blogging as a developer</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Wed, 10 Apr 2024 16:57:15 +0000</pubDate>
      <link>https://dev.to/palmiak/how-to-easily-start-blogging-as-a-developer-3172</link>
      <guid>https://dev.to/palmiak/how-to-easily-start-blogging-as-a-developer-3172</guid>
      <description>&lt;p&gt;So, here you are - you have a great idea for an article or even just wrote it, but you must publish it somewhere. Here are some tips about starting your developer blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  SaaS
&lt;/h2&gt;

&lt;p&gt;Let's start with services that will do the most work for you. You only have to register and focus on writing.&lt;/p&gt;

&lt;p&gt;While those are the quickest to start writing on, always remember that at some point they can change the rules and become paid or add some gatekeeping for readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  dev.to
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dev.to&lt;/strong&gt; is a service that allows you to start blogging in minutes. They focus on dev-related content, so you'll feel in a good place.&lt;/p&gt;

&lt;p&gt;You have to create your posts using markdown (which I consider a good thing) and what's most important it's free.&lt;/p&gt;

&lt;p&gt;You can customize your profile a bit, by setting branding colors.&lt;/p&gt;

&lt;p&gt;Most important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;free&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can export your articles if you would like to migrate it somewhere else&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;others can comment/react to your articles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you &lt;strong&gt;can't&lt;/strong&gt; use your own domain&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like &lt;strong&gt;dev.to&lt;/strong&gt; - unit I started writing regularly here, I was using it from time to time and I liked the experience. I consider this platform a great place to start and a great place to post copies of your articles from different sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-115239.png%2Fb345a7e6e1c680ffed7727487414f458.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-115239.png%2Fb345a7e6e1c680ffed7727487414f458.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;dev.to admin panel&lt;/p&gt;

&lt;h3&gt;
  
  
  Hashnode
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hashnode&lt;/strong&gt; is very similar to &lt;strong&gt;dev.to&lt;/strong&gt; - it allows you to start blogging quickly and for free. The are some differences though. It has a more robust editor (but you can switch to basic markdown), allows you to use custom domains, and has a paid &lt;strong&gt;pro&lt;/strong&gt; version that allows you to use AI and some other features.&lt;/p&gt;

&lt;p&gt;Compared to dev.to you can customize it more. And it also has an amazing headless version, so you can bring your own front end.&lt;/p&gt;

&lt;p&gt;Most important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;can be used for free, but there is a paid version too&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can export your articles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;others can comment/react to your articles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can use your own domain&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like &lt;strong&gt;Hashnode&lt;/strong&gt; too. It's really cool and it evolving quickly. Their headless feature is really cool too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-120511.png%2Fbca0ab98051c4045bc64695934f11417.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-120511.png%2Fbca0ab98051c4045bc64695934f11417.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hashnode admin panel&lt;/p&gt;

&lt;h3&gt;
  
  
  Medium
&lt;/h3&gt;

&lt;p&gt;Some time ago I would also recommend &lt;strong&gt;Medium&lt;/strong&gt;, but some of their practices like requiring readers to create accounts and pay for them each month are preventing me from doing so. In short - don't use it. Dev.to or Hashnode are much better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-hosted solutions
&lt;/h2&gt;

&lt;p&gt;Nothing describes them better than "with great power, comes great responsibility". You are responsible for hosting, security, and making sure your website works. That's not an easy task. On the other hand, you can customize the website as much as you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Astro and other SSGs
&lt;/h3&gt;

&lt;p&gt;Time to enter the &lt;strong&gt;Static Site Generator&lt;/strong&gt; zone. Personally, I recommend &lt;strong&gt;Astro&lt;/strong&gt;, but there are many others like Eleventy, Nuxt, or Next. All of them allow you to write your content using Markdown.&lt;/p&gt;

&lt;p&gt;The main advantage of using them is having full control over your data.&lt;/p&gt;

&lt;p&gt;Also, most of them will give you access to a collection of templates that can be modified in a way you want.&lt;/p&gt;

&lt;p&gt;Using them is one thing - you have to host it somewhere. Don't worry though - there are many places where you can host static sites for free like Vercel, Netlify, CloudlFlare, or GitHub Pages. And even if one will stop being free, it's very easy to migrate from one place to another.&lt;/p&gt;

&lt;p&gt;Most important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;it's free&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you have full control over everything&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can extend it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;things like comments or forms will require 3rd party tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you'll need hosting&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm a huge fan of &lt;strong&gt;SSG&lt;/strong&gt;. And usually, I would recommend one of those. On the other hand, I saw too many cases when developers spent so much time customizing their blogs, that they forgot to write new articles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-121057.png%2F197cd86440f5afbc779aade084cc44b5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-121057.png%2F197cd86440f5afbc779aade084cc44b5.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Code Studio and Astro&lt;/p&gt;

&lt;h3&gt;
  
  
  Statamic
&lt;/h3&gt;

&lt;p&gt;It's time to enter the CMS territory. If you have a bit of experience with PHP, then Statamic might be a great solution for you. It has a few cool starter kits (so you can start writing right away), it's flat-file-based by default and it has an SSG library (so you can host it for free on Vercel or Netlify).&lt;/p&gt;

&lt;p&gt;This is also the way I chose - this blog is created in Statamic. First I used a pre-made starter kit, and over time my wife created this cool look.&lt;/p&gt;

&lt;p&gt;Most important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;it's free for personal use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you have full control of everything&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can extend it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you'll need hosting&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Statamic&lt;/strong&gt; is one of my favorite CMSs out there. You can find some materials about it on this blog too. Highly recommend it if you know PHP basics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-120631.png%2F960afe0e8e3f609c1f71ed56a9cbc4af.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-120631.png%2F960afe0e8e3f609c1f71ed56a9cbc4af.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Statamic admin panel&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress
&lt;/h3&gt;

&lt;p&gt;With WP, you'll just need a bit more fuss to get it working exactly as you want - probably you'll need a code highlighter block and some other plugins to make everything as you imagined. While that's not a problem, you can do it quite quickly, the bigger problem is the database. The thing I loved so much about Statamic was the lack of it. The database adds some problems with moving stuff around from localhost to production (there are plugins that can help here).&lt;/p&gt;

&lt;p&gt;Luckily there are also plugins helping to convert WP into a static website (&lt;strong&gt;Simply Static&lt;/strong&gt;), so you can host WP for free on Netlify, Vercel etc.&lt;/p&gt;

&lt;p&gt;Most important features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;it's free, but some plugins aren't&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you have full control over everything&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can extend it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;there is a plugin that allows you to convert it to a static website&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you'll need hosting&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-121319.png%2F8880e59ba49dd132d67c60d48b1a0841.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fblogging-for-devs%2Fzrzut-ekranu-2024-04-09-121319.png%2F8880e59ba49dd132d67c60d48b1a0841.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WordPress admin panel&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to sum up
&lt;/h2&gt;

&lt;p&gt;If you want to start writing, select a solution that will allow you to start as soon as possible. That's why I would recommend going with &lt;strong&gt;Dev.to&lt;/strong&gt; or &lt;strong&gt;Hashnode&lt;/strong&gt; as they will allow you to start writing in a few minutes. After some time you can decide if they are right for you, or maybe they are limiting you somehow. In that case, you can pick something else.&lt;/p&gt;

&lt;p&gt;Overall, writing is the most important, everything else is just useless fluff.&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>beginners</category>
      <category>writting</category>
    </item>
    <item>
      <title>Front Matter CMS - a bit different approach to content management</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Sun, 24 Sep 2023 11:19:38 +0000</pubDate>
      <link>https://dev.to/palmiak/front-matter-cms-a-bit-different-approach-to-content-management-4nd3</link>
      <guid>https://dev.to/palmiak/front-matter-cms-a-bit-different-approach-to-content-management-4nd3</guid>
      <description>&lt;p&gt;&lt;strong&gt;If you like my content, subscribe to my &lt;a href="https://newsletter.maciekpalmowski.dev"&gt;newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we think about a CMS, the first thing that comes to our mind is WordPress. And even if we aren't thinking about WP, we still see an admin panel somewhere on the internet.&lt;/p&gt;

&lt;p&gt;There is also a group that will say that MarkDown and any editor are good enough for them. They are also right. It can be enough, but working with frontmatter inside MD files can be tedious.&lt;/p&gt;

&lt;p&gt;But what if I tell you that there is a CMS that will transform your Visual Studio Code into a proper CMS with content and media management, taxonomies, and more? Say hello to &lt;a href="https://frontmatter.codes/"&gt;Front Matter CMS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you prefer listening and watching rather than reading - together with Elio Struyf, we did an episode of Code and Coffee Show about &lt;strong&gt;Front Matter CMS&lt;/strong&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Because &lt;strong&gt;FM CMS&lt;/strong&gt; is a Visual Studio Code extension, you must install it first. You'll find two versions - the stable one and the beta one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XKbkTkp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562378726.png/3efda544a8e80929fac0622bad4fd401.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XKbkTkp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562378726.png/3efda544a8e80929fac0622bad4fd401.webp" alt="" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installation, it's time to open a project that you want to use with &lt;strong&gt;Front Matter CMS&lt;/strong&gt;. After doing so, press the FM icon in the sidebar and follow the instructions. At this point, the basic configuration is almost done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aVGYDrbF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562397873.png/8c56f92d041e199fad51c01a64016893.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aVGYDrbF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562397873.png/8c56f92d041e199fad51c01a64016893.webp" alt="" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Sadly, Front Matter CMS can't guess some things. So, before going further, we have to polish the configuration file a bit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mE3E9b9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562670088.png/27f8c38e7122928f956d10503b21b60f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mE3E9b9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562670088.png/27f8c38e7122928f956d10503b21b60f.webp" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the field. Inside of your &lt;code&gt;frontMatter.taxonomy.contentTypes&lt;/code&gt; part, you'll see that some fields are already created. You have to update this part to match the fields you'll need. In my case, I needed to add fields like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cover"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isPreviewImage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Intro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"intro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Author (use the one from src/content/author)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Publishing date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pubDate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datetime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"September 13th, 2023"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isPublishDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dateFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of it is pretty self-explanatory, but there are some more interesting bits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isPreviewImage&lt;/code&gt; - It shows the image as a thumbnail in the Front Matter CMS dashboard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isPublishDate&lt;/code&gt; - This one helps FM CMS to sort posts by date&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;now&lt;/code&gt; - Now is a placeholder that sets the time and date to the moment when you created the post&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information about the fields, I recommend checking this &lt;a href="https://frontmatter.codes/docs/content-creation/fields"&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the case of my Astro theme, there are some extra details I had to add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"previewPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blog/"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"filePrefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"fileType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mdx"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without setting the &lt;code&gt;previewPath&lt;/code&gt;, FM CMS would try to preview blog posts on &lt;code&gt;domain.com/slug&lt;/code&gt; which would return a 404 error because all blog posts are in the &lt;code&gt;blog&lt;/code&gt; folder. Adding this setting solves the problem.&lt;/p&gt;

&lt;p&gt;By default, new files created by FM CMS contain a date as a part of a slug. This is something I'm not a fan of. Luckily, thanks to the &lt;code&gt;filePrefix&lt;/code&gt; setting, we can change it.&lt;/p&gt;

&lt;p&gt;And last but not least - I wanted to create MDX by default - that's why I used &lt;code&gt;fileType&lt;/code&gt; setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing content
&lt;/h2&gt;

&lt;p&gt;Like in any other CMS, you can view your content in the dashboard. Apart from this, you can group them (by year, for example), sort them, or search for a post you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RpRu7KJZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562983433.png/e7a248b24fa91273034ecdd4e873cd3d.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RpRu7KJZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562983433.png/e7a248b24fa91273034ecdd4e873cd3d.webp" alt="" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When viewing a single post, you can access some new powerful features. There are some practical SEO tools or a preview option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LZGRqJLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694563058158.png/edb79271227c516b3b5ecd24410809c6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LZGRqJLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694563058158.png/edb79271227c516b3b5ecd24410809c6.webp" alt="" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also manage images. View them, organize them in folders, and add metadata to every image - like alt texts or descriptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hbR5IUBK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694563021452.png/159cf1dd617556a4502618036c950ed4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hbR5IUBK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694563021452.png/159cf1dd617556a4502618036c950ed4.webp" alt="" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Snippets
&lt;/h2&gt;

&lt;p&gt;Snippets are a fantastic way to stop repeating yourself. It works really great with MDX files and components. So, let's create a simple component that will show &lt;strong&gt;Hello [your name]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The simplest way would be to go to Snippets, press the &lt;strong&gt;Create new snippet&lt;/strong&gt; button, and paste something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'../../components/hello.astro';&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name=&lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and modify the code a bit every time. But we can do better. It's time to reopen our config file and add some fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"frontMatter.content.snippets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple snippet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"import Hello from '../../components/hello.astro;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Hello name=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;[[name]]&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; /&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FM_SELECTED_TEXT"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"frontMatter.snippets.wrapper.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will result in:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yODsGJSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562418033.png/204e21dc8824251b579fbfdf7746acc5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yODsGJSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562418033.png/204e21dc8824251b579fbfdf7746acc5.webp" alt="" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But also, if you select a text and then add the snippet, the selected text will become a default value. Neat.&lt;/p&gt;

&lt;p&gt;There is just one problem with this - there is no way to add a conditional to check if we should import the component again or not. This is something we have to remove manually every time.&lt;/p&gt;

&lt;p&gt;For more use cases, check out the &lt;a href="https://frontmatter.codes/docs/snippets#overview"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom scripts
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of Front Matter CMS is the ability to create your own scripts that can do whatever you want. I will show you a very simple example of how to make your title uppercase with a click of a button.&lt;/p&gt;

&lt;p&gt;This is the script we'll use - I placed it in the scripts folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// getting all the frontmatter args&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frontMatterArg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// transorming to json&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frontMatterArg&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;frontMatterArg&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frontMatterArg&lt;/span&gt;&lt;span class="p"&gt;)&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="c1"&gt;// making the title uppercase&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// updating the contnet&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;frontmatter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see - it gets the data, changes the title, and creates a JSON that is passed to &lt;code&gt;console.log&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;Now, let's add this script to FM CMS. We have to open the configuration file again and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"frontMatter.custom.scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Uppercase title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./scripts/social-img.cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will result in this cool-looking button that will uppercase your title.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mE3E9b9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562670088.png/27f8c38e7122928f956d10503b21b60f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mE3E9b9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/fm_cms/screely-1694562670088.png/27f8c38e7122928f956d10503b21b60f.webp" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just press the "Uppercase title button"&lt;/p&gt;

&lt;p&gt;But you can do more with custom scripts - check &lt;a href="https://frontmatter.codes/docs/custom-actions#overview"&gt;this&lt;/a&gt; documentation section to learn more about custom scripts.&lt;/p&gt;

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

&lt;p&gt;I hope I showed you how cool Front Matter CMS can be. While it's not a CMS for everyone, I think that developers will find it very useful. Also, they won't have to leave their favorite IDE.&lt;/p&gt;

&lt;p&gt;As for less technical people - if someone will configure it for them, FM CMS might be a really easy-to-use solution.&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>cms</category>
      <category>contentwriting</category>
    </item>
    <item>
      <title>Different flavors of content management</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Mon, 28 Aug 2023 13:40:20 +0000</pubDate>
      <link>https://dev.to/palmiak/different-flavors-of-content-management-5an4</link>
      <guid>https://dev.to/palmiak/different-flavors-of-content-management-5an4</guid>
      <description>&lt;p&gt;Picking up a perfect CMS isn't an easy task. You have to consider many things before picking the one and only. Here is a small guide explaining the different aspects of every CMS.&lt;/p&gt;

&lt;p&gt;Most people widely know the term Content Management System (or CMS for short). But many people don't see how many CMS flavors there are. I hope to list and explain the pros and cons of most possible approaches below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless vs. Monolith
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;monolith&lt;/strong&gt; CMS is a CMS that is responsible both for managing data (you can create posts, add images, etc.) and for showing this data to others (using some templating system). &lt;a href="https://wordpress.org"&gt;WordPress&lt;/a&gt;, &lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt;, &lt;a href="https://craftcms.com/"&gt;CraftCMS&lt;/a&gt;, or &lt;a href="https://www.drupal.org/"&gt;Drupal&lt;/a&gt; are perfect examples of monolith CMSs (although all can go headless too).&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;headless&lt;/strong&gt; one is responsible only for data management and providing an API for other applications to show this data. When talking about headless CMS, &lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt; or &lt;a href="https://www.sanity.io/"&gt;Sanity&lt;/a&gt; comes to my mind first, but there are many more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f44d1yqQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692350858678.png/7ece33a2c2c103b69056a579bf2f6b6c.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f44d1yqQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692350858678.png/7ece33a2c2c103b69056a579bf2f6b6c.webp" alt="Statamic can work both as headless and monolith CMS" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you know the theory, but when should you use them? Monolithic CMSs are usually simpler to use because you get everything working out of the box. On the downside, you have to use the tech in which the CMS is created (you'll use &lt;strong&gt;PHP&lt;/strong&gt; for WordPress or &lt;strong&gt;Ruby&lt;/strong&gt; for Camaleon).&lt;/p&gt;

&lt;p&gt;On the other hand, headless CMSs start to shine when our setup gets a bit more complicated, or our development team would like to use a different technology than the one in which the CMS is created. The biggest cons? You'll have some additional work orchestrating everything.&lt;/p&gt;

&lt;p&gt;You should also check my article &lt;strong&gt;&lt;a href="https://maciekpalmowski.dev/is-going-headless-worth-the-fuss/"&gt;about headless&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-hosted vs. SaaS
&lt;/h2&gt;

&lt;p&gt;Another way in which you can differentiate CMSs is the way how they are hosted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-hosted&lt;/strong&gt; solutions give you complete control over your data. You can do everything with it. But with great power comes great responsibility - hosting, updates, backups, etc. If you mess something up, it's all on you. Also, you have to set everything up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org"&gt;WordPress&lt;/a&gt;, &lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt;, or &lt;a href="https://craftcms.com/"&gt;CraftCMS&lt;/a&gt; are great examples of self-hosted solutions.&lt;/p&gt;

&lt;p&gt;When you decide to go the &lt;strong&gt;SaaS&lt;/strong&gt; route, in theory, the product should got you covered with all the backups and other responsibilities. You should only focus on your website. There are some potential downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You might need a feature that your SaaS won't provide&lt;/li&gt;
&lt;li&gt;  The pricing may change&lt;/li&gt;
&lt;li&gt;  The company behind the SaaS might not fulfill its responsibilities, and you might learn you lost all your data.&lt;/li&gt;
&lt;li&gt;  They might sunset the product, and you'll have to move somewhere else quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://webflow.com/"&gt;Webflow&lt;/a&gt;, &lt;a href="https://www.storyblok.com/home"&gt;Storyblok&lt;/a&gt;, and &lt;a href="https://buttercms.com/"&gt;ButterCMS&lt;/a&gt; are great examples of SaaS CMSs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm_d9Cmm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692350964341.png/de7b2cf6bc3c728743032f8e8f099e48.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm_d9Cmm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692350964341.png/de7b2cf6bc3c728743032f8e8f099e48.webp" alt="Sanity.io" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Content editing vs. visual editing
&lt;/h2&gt;

&lt;p&gt;Some CMSs only allow you to &lt;strong&gt;edit content&lt;/strong&gt; placed somewhere inside premade themes or templates. The biggest pro of such a way is also its biggest con. With such an approach, there is a separation of concerns - the CMS is only responsible for the content, while how it is presented is totally on the template end. You have to ask the developer if you want a more significant change. On the other hand, if made correctly, it helps keep the website more consistent and prevents destroying the UX created by the team responsible for this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt;, &lt;a href="https://craftcms.com/"&gt;CraftCMS&lt;/a&gt;, and &lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt; are perfect examples here.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;visual editing&lt;/strong&gt;, you have much more flexibility. You aren't only capable of changing the content - you can also change the way everything is presented. This approach gives the user much more freedom, but such freedom can destroy the website's UX in the wrong hands. On the other hand, you won't have to ask the developer about every change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org"&gt;WordPress&lt;/a&gt; (with Page Builders or Full Site Editing) or &lt;a href="https://www.storyblok.com/home"&gt;Storyblok&lt;/a&gt; are perfect examples in this category.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ubBzITxc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692351218563.png/0e5530031c8712c0b93f1f506e4b74f5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ubBzITxc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692351218563.png/0e5530031c8712c0b93f1f506e4b74f5.webp" alt="WordPress Block Editor AKA Gutenberg" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote vs. local
&lt;/h2&gt;

&lt;p&gt;The most typical approach is having a CMS admin panel sit somewhere on the server; everyone with an account uses this. This is a very convenient approach, especially when working with a team. This way, many people can work on different articles simultaneously without worrying about potential conflicts or overwriting stuff. The only con is related to security - everyone can try to get inside, and if you forget to update our CMS or some user have a weak password, it can be someone outside of our team. &lt;a href="https://wordpress.org"&gt;WordPress&lt;/a&gt;, &lt;a href="https://www.drupal.org/"&gt;Drupal&lt;/a&gt;, &lt;a href="https://craftcms.com/"&gt;CraftCMS&lt;/a&gt;, or &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt; are perfect examples of such CMSs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local&lt;/strong&gt; CMSs are the ones that are mostly file-based (like &lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt; or &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;). This means that you can edit everything locally and deploy the data. This way, our CMS is more secure, but on the downside, you have to have a local server working, and you might experience more conflicts, especially when two people will work on the same article (although Git might save you from many of those). It also means that there is a higher learning curve. A remote CMS works somewhere on a server, and most users don't care how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dedicated app vs. bring your own app
&lt;/h2&gt;

&lt;p&gt;When discussing CMSs, the first thing that comes to mind is an &lt;strong&gt;admin panel&lt;/strong&gt;, where you create and edit our content. And most CMSs work that way. Why? Because it is convenient and it's easier to teach your team how to use it. The only downside is that you have to get used to the admin panel, and sometimes you might not have any option to customize the experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vbi6iL4e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692351348113.png/074e5b8ba3a1d93c63060198f04b383e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vbi6iL4e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692351348113.png/074e5b8ba3a1d93c63060198f04b383e.webp" alt="Nuxt Studio" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, when your CMS is based on &lt;strong&gt;Markdown files&lt;/strong&gt;, you can use whatever editor you want. For example, if you use Nuxt as your CMS, everyone can choose which editor they prefer. It can be Visual Studio Code, &lt;a href="https://frontmatter.codes/"&gt;FrontMatter CMS&lt;/a&gt;, or maybe &lt;a href="https://nuxt.studio/"&gt;Nuxt Studio&lt;/a&gt;. Everyone can pick the app they like the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  WordPress vs. the rest
&lt;/h2&gt;

&lt;p&gt;It might sound funny, but WordPress has &lt;strong&gt;~60%&lt;/strong&gt; of the CMS market share. Shopify is second with &lt;strong&gt;6%&lt;/strong&gt;. &lt;a href="https://w3techs.com/technologies/history_overview/content_management/ms/y"&gt;Here&lt;/a&gt; is the source of those numbers.&lt;/p&gt;

&lt;p&gt;We might discuss the methodology and the fact that WordPress is easy to discover, while some CMSs aren't, but it doesn't change the fact that WP is playing a totally different game today. But we also have to remember that it wasn't like that.&lt;/p&gt;

&lt;p&gt;I just found a report from 2008 where we can read:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At this point in time, the only thing we can state with confidence is that the battle for market dominance has yet to be settled. Indeed, it rather looks like the battle is just about to heat up! WordPress, Joomla! and Drupal share a significant lead over the other products in the open source CMS market. We do not expect that to change in the near to medium term. Whether one will emerge as the market leader remains to be seen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Who knows what the market will look like 10 years from now?&lt;/p&gt;

&lt;h2&gt;
  
  
  Git-based vs. API-driven
&lt;/h2&gt;

&lt;p&gt;Big thanks to &lt;a href="https://twitter.com/avidlarge"&gt;David Large&lt;/a&gt; for mentioning this distinction for headless CMSs.&lt;/p&gt;

&lt;p&gt;Git-based CMS are the ones that use Git to manage many aspects of CMS - mostly related to collaboration and history. This is a great solution because we aren't reinventing the wheel, but we're using an amazing standard for stuff like this.&lt;/p&gt;

&lt;p&gt;Also, we have full control over our content.&lt;/p&gt;

&lt;p&gt;Solutions like &lt;a href="https://cloudcannon.com/"&gt;CloudCanon&lt;/a&gt; or &lt;a href="https://tina.io/"&gt;TinaCMS&lt;/a&gt; use this approach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SKeenqk5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692961268624.png/81f5811d4ff88f54cd5146d49d0adb8c.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SKeenqk5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692961268624.png/81f5811d4ff88f54cd5146d49d0adb8c.webp" alt="TinaCMS" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;API-driven CMSs provide you with an API that you have to connect with your front end. Those CMSs are very often more flexible. They are also great if we want to provide data to multiple sources (our website, store, and mobile application).&lt;/p&gt;

&lt;p&gt;We can control our data (self-hosted solution like &lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt;) or not (&lt;a href="https://www.contentful.com/"&gt;Contentful&lt;/a&gt;) based on the chosen solution.&lt;/p&gt;

&lt;p&gt;Those CMSs, in most cases, are more difficult to start with and require a developer for any bigger change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Site Builders vs. App Frameworks
&lt;/h2&gt;

&lt;p&gt;Big thanks to &lt;a href="https://twitter.com/sbwalker"&gt;Shaun Walker&lt;/a&gt; for mentioning this distinction.&lt;/p&gt;

&lt;p&gt;It might seem that it is similar to &lt;strong&gt;Content editing vs. visual editing&lt;/strong&gt;, but not completely. Content vs. Visual editing is more about how we edit the content. Here, we are looking more at the CMS's focus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Site builders&lt;/strong&gt; are solutions that should enable you to build websites without technical knowledge (in theory). They are amazing if your data structure is quite simple and you want a working website quickly. Those will be perfect if you want a landing page, a blog, a company page, or a simple store. Those solutions either have a lot of predefined blocks, or you can create everything manually.&lt;/p&gt;

&lt;p&gt;There are some builders that are more customizable, enabling you to add your own components/blocks.&lt;/p&gt;

&lt;p&gt;Webflow and &lt;a href="https://builder.io"&gt;Builder.io&lt;/a&gt; are perfect examples here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KFtYot0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692962457417.png/106dd714bb8c9ed88c7f81a6bbaf6ff8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KFtYot0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://maciekpalmowski.dev/img/containers/assets/arts/cms-types/screely-1692962457417.png/106dd714bb8c9ed88c7f81a6bbaf6ff8.webp" alt="Builder.io" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;App Frameworks are CMSs that can be extended in whatever way you want. If we take a look at what we can achieve with &lt;a href="https://wordpress.org"&gt;WordPress&lt;/a&gt;, &lt;a href="https://statamic.com/"&gt;Statamic&lt;/a&gt;, &lt;a href="https://craftcms.com/"&gt;CraftCMS&lt;/a&gt;, or &lt;a href="https://www.drupal.org/"&gt;Drupal&lt;/a&gt;, we'll see that there are almost no limits. They are used for a personal blog but also can become a center of our very advanced application managing different parts of our business.&lt;/p&gt;

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

&lt;p&gt;As I mentioned initially, picking up a perfect CMS isn't easy. Also, everything I said is just the tip of the iceberg. It's one of those "it depends" situations where you must analyze your needs and decide based on those.&lt;/p&gt;

&lt;p&gt;Which CMSs I'm using right now?&lt;/p&gt;

&lt;p&gt;I'm using Statamic for this blog - it's only on my local machine and converted to a static website during deployment. Why? It's a small blog, and I don't want to worry about security and other stuff.&lt;/p&gt;

&lt;p&gt;I'm also using Substack (a SaaS) for my newsletter. Why? Because I'm lazy and I wanted to focus only on the content.&lt;/p&gt;

&lt;p&gt;Did you enjoyed reading this post? If so, don't forget to subscribe to my &lt;a href="https://newsletter.maciekpalmowski.dev"&gt;newsletter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cms</category>
      <category>webdev</category>
      <category>wordpress</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Creating custom actions in Buddy</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Mon, 02 Jan 2023 15:57:48 +0000</pubDate>
      <link>https://dev.to/palmiak/creating-custom-actions-in-buddy-kfm</link>
      <guid>https://dev.to/palmiak/creating-custom-actions-in-buddy-kfm</guid>
      <description>&lt;p&gt;While Buddy was always my favorite CI/CD app, it was missing one feature - shareable custom actions. Luckily for us - it has changed lately and in this article, I will show you how to create them.&lt;/p&gt;

&lt;p&gt;I always saw the lack of Custom Actions as the biggest downside of Buddy. There was an action &lt;strong&gt;Custom Build&lt;/strong&gt;, but it didn't provide us with any UI options, and it was pretty tedious to share and keep updated.&lt;/p&gt;

&lt;p&gt;Luckily, Custom Actions change all of this. From now, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;create a UI to make it easier to use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;share it with others&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;import someone's actions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to store Custom Actions
&lt;/h2&gt;

&lt;p&gt;There are three ways to do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;create a separate repository for all your custom actions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create a separate repository for each action&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add actions to the project's repositories&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each is useful in some cases. A separate repository for all your actions is excellent when you work in a company and want to have all your actions in one place.&lt;/p&gt;

&lt;p&gt;If you are considering an &lt;strong&gt;Open Source approach&lt;/strong&gt;, having separate actions in separate repositories seems best.&lt;/p&gt;

&lt;p&gt;But, if you want to create related actions with a project, then adding them to that project's repo is the best solution.&lt;/p&gt;

&lt;p&gt;For more information, check out the &lt;a href="https://buddy.works/docs/pipelines/custom-actions"&gt;official Buddy documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create a Custom Action
&lt;/h2&gt;

&lt;p&gt;Again, you have a choice. You can either create everything manually based on the documentation or use this &lt;a href="https://www.npmjs.com/package/buddy-custom-action"&gt;small tool&lt;/a&gt; I made.&lt;/p&gt;

&lt;p&gt;If you decide to use &lt;strong&gt;Buddy Custom Action CLI,&lt;/strong&gt; you only need to run &lt;code&gt;npx buddy-custom-action@latest&lt;/code&gt; and answer a few questions. When you are done, you should have a ready-to-use boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Jo7scRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25289%2529.png/2f4bbebc93cfcf9b6764bcf974f9701e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Jo7scRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25289%2529.png/2f4bbebc93cfcf9b6764bcf974f9701e.webp" alt="" width="620" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Custom actions&lt;/p&gt;

&lt;h2&gt;
  
  
  Example #1 - No deployment on Friday action
&lt;/h2&gt;

&lt;p&gt;Some time ago, I created this simple action that &lt;a href="https://maciekpalmowski.dev/no-deploy-friday-action/"&gt;prevents deploying on Fridays&lt;/a&gt;. Let's try to convert it into a Custom Action.&lt;/p&gt;

&lt;p&gt;First, I used the &lt;code&gt;npx buddy-custom-action@latest&lt;/code&gt;. I only provided it with a name and a short description, and I said yes to creating an execute commands section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    name: "no_deploys_on_friday"
    execute_commands:
      - 
    docker_image_name: "ubuntu"
    docker_image_tag: "latest"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my boilerplate. Of course, right now, it won't do anything. It's time to add the missing command itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    name: "no_deploys_on_friday"
    execute_commands:
      - if [[ $(date +%u) == 5 ]]; then echo 'Do not deploy on Firday.' &amp;amp;&amp;amp; exit 1; fi;
    docker_image_name: "ubuntu"
    docker_image_tag: "latest"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is this all? No - we should also add an icon and name it &lt;code&gt;action.png&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we are done. It was simple. Now it's time to create something more complicated.&lt;/p&gt;

&lt;p&gt;You can download the code from &lt;strong&gt;&lt;a href="https://github.com/palmiak/custom-actions-article"&gt;this repository&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example #2 - API Parser
&lt;/h2&gt;

&lt;p&gt;This time, we'll create something more complicated. This action will get data from a given API then it will be parsed with &lt;code&gt;jq&lt;/code&gt; and in the end, we will pass the result to another action.&lt;/p&gt;

&lt;p&gt;Again, let's get started with running the &lt;code&gt;npx buddy-custom-action@latest&lt;/code&gt;. We'll also need inputs and outputs this time.&lt;/p&gt;

&lt;p&gt;Let's start with the inputs. Inputs are the fields that you can populate with some data. In our case, we will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the API URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the &lt;code&gt;jq&lt;/code&gt; command to filter out the required value&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    inputs:
      url:
        name: "URL you want to test"
        required: true
      jq_command:
        name: "JQ parse command"
        required: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will use the Star Wars API and filter out the character's name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uY8qkGLx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25287%2529.png/b6ee2bc528deca117e806ec8cf272799.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uY8qkGLx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25287%2529.png/b6ee2bc528deca117e806ec8cf272799.webp" alt="" width="620" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to add the &lt;code&gt;exectute_commands&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    execute_commands:
      - apt-get update &amp;amp;&amp;amp; apt-get install -y curl jq
      - api=`curl -s $url`
      - `jq $jq_command &amp;lt;&amp;lt;&amp;lt; $api`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works - we will get &lt;strong&gt;Luke Skywalker&lt;/strong&gt; as a result. But, while it works, this action is a bit useless. It would be much better to pass the result to other actions. Let's do it, then.&lt;/p&gt;

&lt;p&gt;This is when outputs come in handy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    output:
      variables:
        result:
          info: "Result of JQ on API"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will result in a tiny change in how your action looks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aDABJ1Ey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25288%2529.png/3490337ff6ba8feb8ecc3ba601797e7a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aDABJ1Ey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://maciekpalmowski.dev/img/containers/assets/arts/custom-actions/control-v-%25288%2529.png/3490337ff6ba8feb8ecc3ba601797e7a.webp" alt="" width="620" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we have to change the &lt;code&gt;execute_commends&lt;/code&gt; too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    execute_commands:
      - apt-get update &amp;amp;&amp;amp; apt-get install -y curl jq
      - api=`curl -s $url`
      - result=`jq $jq_command &amp;lt;&amp;lt;&amp;lt; $api`
      - export result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this point, we can use the &lt;code&gt;result&lt;/code&gt; variable in other actions.&lt;/p&gt;

&lt;p&gt;The whole action should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    name: "API_Parser"
    inputs:
      url:
        name: "URL you want to test"
        required: true
      jq_command:
        name: "JQ parse command"
        required: true
    output:
      variables:
        result:
          example: "Variable with the results of the JQ on API"
    execute_commands:
      - apt-get update &amp;amp;&amp;amp; apt-get install -y curl jq
      - api=`curl -s $url`
      - result=`jq $jq_command &amp;lt;&amp;lt;&amp;lt; $api`
      - export result
    docker_image_name: "ubuntu"
    docker_image_tag: "latest"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can download the code from &lt;strong&gt;&lt;a href="https://github.com/palmiak/custom-actions-article"&gt;this repository&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else can you do?
&lt;/h2&gt;

&lt;p&gt;The sky is the limit here. I see &lt;strong&gt;Custom Actions&lt;/strong&gt; as one of the most groundbreaking features in Buddy, and it gives everyone the potential to create and share their own actions. We don't have to wait until developers have time to implement something - now, we can do it independently.&lt;/p&gt;

&lt;p&gt;I can't wait to see what actions you will create.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>buddy</category>
      <category>ci</category>
      <category>devops</category>
    </item>
    <item>
      <title>Is it worth migrating from Revue to Substack?</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Wed, 28 Dec 2022 11:50:13 +0000</pubDate>
      <link>https://dev.to/palmiak/is-it-worth-migrating-from-revue-to-substack-3h8k</link>
      <guid>https://dev.to/palmiak/is-it-worth-migrating-from-revue-to-substack-3h8k</guid>
      <description>&lt;p&gt;As you probably already know, Revue is closing on 18 January 2023, and some of you are already looking for an alternative. I will try to explain if Substack is a good alternative.&lt;/p&gt;

&lt;p&gt;When Elon Musk bought Twitter, there were a few rumors that &lt;strong&gt;Revue&lt;/strong&gt; would be closed. So back then, I already started to look for an alternative.&lt;/p&gt;

&lt;p&gt;In my case, I had a few requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It shouldn't be too expensive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It should be simple. I want to focus only on writing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It should have the possibility to connect my own domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It should have a nice writing experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SaaS, I didn't want to manage anything.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds simple, right? I was also surprised to learn that finding something like this is difficult.&lt;/p&gt;

&lt;p&gt;After a week of testing, I decided to go with &lt;a href="https://substack.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Substack&lt;/strong&gt;&lt;/a&gt;. You can see the results of my hard work &lt;a href="https://newsletter.maciekpalmowski.dev" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing all the requirements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pricing and my own domain
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Substack&lt;/strong&gt; can be free. You only pay if you have paid newsletters, but that's not my case. I only had to pull my credit card when I wanted to connect my domain. It was a one-time payment, and it was about $20.&lt;/p&gt;

&lt;p&gt;Comparing the experience of connecting your domain in &lt;strong&gt;Revue&lt;/strong&gt; and on &lt;strong&gt;Substack&lt;/strong&gt;, I can say that paying those $20 was a pleasure.&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%2F6sqtms8obboi516nf4n9.gif" 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%2F6sqtms8obboi516nf4n9.gif" width="334" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplicity
&lt;/h3&gt;

&lt;p&gt;While &lt;strong&gt;Substack&lt;/strong&gt; has far more options than Revue, it's still straightforward. The migration process, setting up my theme, etc., was a breeze. I didn't have to use any extra documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing experience
&lt;/h3&gt;

&lt;p&gt;In general, &lt;strong&gt;Substack&lt;/strong&gt; writing experience is &lt;strong&gt;really nice&lt;/strong&gt;. The editor is &lt;strong&gt;quite zen&lt;/strong&gt; and lets you focus on writing. One really cool thing is the set of premade blocks that can help you boost engagement.&lt;/p&gt;

&lt;p&gt;There is one thing where Substack is losing with Revue - Revue has this great link block that downloads the open graph data.&lt;/p&gt;

&lt;p&gt;Also, it would've been nice if Substack would support Markdown.&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%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsubstack%2Fcontrol-v-%25285%2529.png%2Fca5586dadd16296c6ebd6d41b24742d7.webp" 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%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsubstack%2Fcontrol-v-%25285%2529.png%2Fca5586dadd16296c6ebd6d41b24742d7.webp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how the editor looks like&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Migration was a fantastic experience&lt;/strong&gt;. Why? Because it only took me about one hour, I never felt lost during the process. Keep in mind that this is the first time I was doing something serious on Substack, so I consider the amount of time needed very short.&lt;/p&gt;

&lt;h2&gt;
  
  
  It has a big flaw and one small one
&lt;/h2&gt;

&lt;p&gt;Can you imagine that this &lt;strong&gt;almost perfect newsletter system lacks API&lt;/strong&gt;? I'm not kidding. I learned about this when I wanted to automate something when I discovered this thing.&lt;/p&gt;

&lt;p&gt;I was sure that all newsletter apps have at least a simple API. But not here. I probably understand why - Substack wants to be a place where you focus on writing, not where you send your corporate notifications for free.&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%2Fuzw8evjral27z9rbtha8.gif" 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%2Fuzw8evjral27z9rbtha8.gif" alt="Are you kidding me" width="480" height="400"&gt;&lt;/a&gt;Also, the &lt;strong&gt;statistics module could be better&lt;/strong&gt;. It's not bad, but there is room for improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I recommend moving to Substack?
&lt;/h2&gt;

&lt;p&gt;It all depends if you need the API or not. If not, I think &lt;strong&gt;Substack is as closest to perfection as possible&lt;/strong&gt;. I've been using it for 4 weeks, and I really like it. I really enjoy the moment when I'm preparing my weekly newsletter there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What other applications did I check?
&lt;/h2&gt;

&lt;p&gt;I started with big names like &lt;strong&gt;Mailchimp&lt;/strong&gt;, &lt;strong&gt;MailerLite,&lt;/strong&gt; and &lt;strong&gt;GetResponse&lt;/strong&gt; - they are much better for corporate usage than a small, personal newsletter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buttondown&lt;/strong&gt; - I loved the possibility of writing using markdown, but their free tier is kind of poor, and the UI was a bit too old-fashioned for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beehiiv&lt;/strong&gt; - it has a lot of features and a nice free tier, but it has too many options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Newsletter Glue&lt;/strong&gt; - NG is a WordPress plugin. It has everything I need, but I would have to host it on my own. If it weren't for &lt;strong&gt;Substack&lt;/strong&gt;, I would probably install WordPress somewhere and install Newsletter Glue there.&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Playing around with Sanity.io</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Thu, 15 Dec 2022 09:24:07 +0000</pubDate>
      <link>https://dev.to/palmiak/playing-around-with-sanityio-3l5e</link>
      <guid>https://dev.to/palmiak/playing-around-with-sanityio-3l5e</guid>
      <description>&lt;p&gt;Sanity.io is one of the new cool kids regarding headless CMSs. I decided to give it a try and create almost precisely the same blog as the one you are reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Sanity.io
&lt;/h2&gt;

&lt;p&gt;First of all, Sanity.io is a &lt;strong&gt;headless CMS&lt;/strong&gt;. This means it's only responsible for storing the data, and it's up to us to connect it with a front end. We can use whatever we want here.&lt;/p&gt;

&lt;p&gt;The CMS is fully configurable. Thanks to &lt;strong&gt;Sanity Studio,&lt;/strong&gt; we can configure our CMS how we want - we just need to create schemas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsanity%2Fsanity-admin-panel.png%2F019f41a0d9b079ce739b2a28e3587313.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsanity%2Fsanity-admin-panel.png%2F019f41a0d9b079ce739b2a28e3587313.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity also gives us two ways to communicate with our application: &lt;strong&gt;GraphQL&lt;/strong&gt; or &lt;strong&gt;GROQ&lt;/strong&gt;. I just want to say that &lt;strong&gt;GROQ&lt;/strong&gt; quickly became my favorite. It's like SQL and GraphQL had a baby.&lt;/p&gt;

&lt;p&gt;Now that we know what &lt;strong&gt;Sanity.io&lt;/strong&gt; is, it's time to code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plan
&lt;/h2&gt;

&lt;p&gt;I plan to create a blog like the one you are just reading. This means we will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;posts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;navigation menus&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;setting page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;categories&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pages (although I skipped this as even now I'm almost not using this)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also some things that I decided not to add because it's just an experiment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SEO settings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open Graph data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Time to start modeling this content using &lt;strong&gt;Sanity Studio&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and some groundwork
&lt;/h2&gt;

&lt;p&gt;Installation is pretty simple. Just follow the steps from the &lt;a href="https://www.sanity.io/docs/create-a-sanity-project" rel="noopener noreferrer"&gt;official guide&lt;/a&gt;, and everything should be up and running in a few minutes.&lt;/p&gt;

&lt;p&gt;In my case, I also installed one addon - the &lt;a href="https://www.sanity.io/plugins/sanity-plugin-markdow" rel="noopener noreferrer"&gt;markdown input field&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And here is the &lt;a href="https://github.com/palmiak/astronity" rel="noopener noreferrer"&gt;code repository&lt;/a&gt;. In the &lt;strong&gt;studio&lt;/strong&gt; folder, you'll find everything related to &lt;strong&gt;Sanity&lt;/strong&gt;. Apart from this folder, everything else is associated with &lt;strong&gt;Astro&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding schemas
&lt;/h2&gt;

&lt;p&gt;My schema structure looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;schemas
|-- documents
|-- objects
index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;document&lt;/strong&gt; folder is for post types, and the &lt;strong&gt;objects&lt;/strong&gt; folder is for small bits that we will reuse from time to time.&lt;/p&gt;

&lt;p&gt;For example, let me show you how to create a category schema. First, create an empty file &lt;code&gt;category.js&lt;/code&gt; inside the &lt;code&gt;schemas/documents&lt;/code&gt; folder. Then add this code inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
    name: "category",
    type: "document",
    title: "Category",
    fields: [
        {
            name: "title",
            type: "string",
            title: "Title",
        },
        {
            name: "slug",
            type: "slug",
            title: "Slug",
            description:
                "Some frontends will require a slug to be set to be able to show the category",
            options: {
                source: "title",
                maxLength: 96,
            },
        },
    ],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, it consists of two fields - Title and Slug.&lt;/p&gt;

&lt;p&gt;Next, it's time to import it the &lt;code&gt;index.js&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import category from "./documents/category";

export const schemaTypes = [category];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And is how our admin panel will look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsanity%2Fcontrol-v-%25284%2529.png%2Fcd2c7f4bbf748b248c60454209a5661a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fsanity%2Fcontrol-v-%25284%2529.png%2Fcd2c7f4bbf748b248c60454209a5661a.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can browse the rest of the fields in the &lt;a href="https://github.com/palmiak/astronity" rel="noopener noreferrer"&gt;GitHub repo.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Sanity.io with Astro
&lt;/h2&gt;

&lt;p&gt;Now, it's time to connect &lt;strong&gt;Sanity.io&lt;/strong&gt; with &lt;strong&gt;Astro&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I won't go step-by-step because everything becomes simple if you understand how the GROQ language works.&lt;/p&gt;

&lt;p&gt;First, we need to install the &lt;a href="https://www.sanity.io/plugins/astro-sanity" rel="noopener noreferrer"&gt;Sanity + Astro&lt;/a&gt; plugin. Kudos to Jaydan Urwin for all his hard work. After installing the plugin, I created a &lt;code&gt;/src/utils/sanityHelper.js&lt;/code&gt; file to add all the helper functions I need.&lt;/p&gt;

&lt;p&gt;So, let's get the navigation. First of all, I created a helper:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function getNavigation(navigationName) {
    const query = `*[_type == "nav" &amp;amp;&amp;amp; id == "${navigationName}" ]`;
    const navigation = await useSanityClient().fetch(query);
    return navigation;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It fetches all the posts from the &lt;code&gt;nav&lt;/code&gt; post type and the passed navigation id.&lt;/p&gt;

&lt;p&gt;Next, in my &lt;code&gt;header&lt;/code&gt; component I'm doing something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { getNavigation } from "../utils/sanityHelper.js";
const navigation = await getNavigation( 'header_menu' );
---
&amp;lt;ul class="font-bold font-palmiak-font tracking-widest"&amp;gt;
{navigation[0].navItems.map((item) =&amp;gt; (
    &amp;lt;MenuHeaderMobile href={item.navItemUrl.linkUrl}&amp;gt;{item.text}&amp;lt;/MenuHeaderMobile&amp;gt;
))}
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;... and that's it :)&lt;/p&gt;

&lt;p&gt;Fetching posts is similar. There are some gotchas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Images
&lt;/h3&gt;

&lt;p&gt;In the helper file, you'll find the &lt;code&gt;urlForImage&lt;/code&gt; function. To use, you have to use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{urlForImage(post.mainImage).width(640).auto("format")}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Also, check the &lt;a href="https://www.sanity.io/docs/image-url" rel="noopener noreferrer"&gt;sanity/image-url&lt;/a&gt; documentation. You'll learn more about all its possibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Markdown fields
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt;, by default, doesn't have any method to show markdown data that was fetched from an API. To fix this, we have you use the &lt;code&gt;marked&lt;/code&gt; package. This is how I displayed post content:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { marked } from 'marked';
const { post } = Astro.props as Props;
---
&amp;lt;div set:html={marked.parse(post.body)} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Querying references
&lt;/h3&gt;

&lt;p&gt;When I wanted to display category names in every post, I hit a small bump on the road. Luckly it was easy to solve.&lt;/p&gt;

&lt;p&gt;This is what my initial query looked like:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*[_type == "post" ] {
    _id, 
    title, 
    publishedAt, 
    categories,
    mainImage, 
    excerpt,
    slug
} | order(publishedAt desc)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to get all the data related to each category, I had to add &lt;code&gt;[]-&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*[_type == "post" ] {
    _id, 
    title, 
    publishedAt, 
    categories[]-&amp;gt;,
    mainImage, 
    excerpt,
    slug
} | order(publishedAt desc)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check out &lt;a href="https://www.sanity.io/docs/how-queries-work" rel="noopener noreferrer"&gt;this page&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;I hope you'll also find &lt;strong&gt;Sanity.io&lt;/strong&gt; as friendly and easy to use as me. Of course, this article just scratches the surface of what &lt;strong&gt;Sanity.io&lt;/strong&gt; can do and how we can configure it.&lt;/p&gt;

&lt;p&gt;Don't forget to share what you have done using &lt;strong&gt;Sanity.io&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>sanity</category>
      <category>astro</category>
      <category>headless</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>The world outside of WordPress</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Wed, 30 Nov 2022 02:17:43 +0000</pubDate>
      <link>https://dev.to/palmiak/the-world-outside-of-wordpress-1np3</link>
      <guid>https://dev.to/palmiak/the-world-outside-of-wordpress-1np3</guid>
      <description>&lt;p&gt;&lt;strong&gt;WordPress has more than 40% of the CMS market share. But we are not alone in the IT world. There is a lot happening outside of WordPress.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My background
&lt;/h2&gt;

&lt;p&gt;I was a &lt;strong&gt;PHP developer&lt;/strong&gt; specializing in WordPress for most of my career. If you are following me on social media, you probably noticed that more than a year ago, I switched from development to DevRel. With this switch, I had to break out of the &lt;strong&gt;WordPress bubble&lt;/strong&gt;. Don't get me wrong - I still love WordPress and most of the community behind it. But...&lt;/p&gt;

&lt;h2&gt;
  
  
  There is a world outside.
&lt;/h2&gt;

&lt;p&gt;It's big and overwhelming and sometimes scary. But you know what? It's also fun, engaging, and very refreshing. Because I'm a &lt;strong&gt;DevRel&lt;/strong&gt;, I don't have many chances to focus on something particular. Still, I'm having a lot of fun exploring different CMSs (like &lt;a href="https://statamic.com/" rel="noopener noreferrer"&gt;Statamic&lt;/a&gt;, &lt;a href="https://craftcms.com/" rel="noopener noreferrer"&gt;Craft&lt;/a&gt;, or &lt;a href="https://sanity.io" rel="noopener noreferrer"&gt;Sanity&lt;/a&gt;), new approaches (at last, I understood why the &lt;strong&gt;headless approach&lt;/strong&gt; is so important), and diving into tech I never used before (hello &lt;a href="https://buildpacks.io/" rel="noopener noreferrer"&gt;Buildpacks&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%2Flmxcaql1xbc31d51e3a0.gif" 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%2Flmxcaql1xbc31d51e3a0.gif" width="482" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, this new world is also full of dramas, but it's a great place to learn something new. To compare how others lead their projects, work as a community, and deal with problems.&lt;/p&gt;

&lt;p&gt;I can't wait for 2023, when I'm planning to visit quite a few conferences - most of them will be non-WordPress. This will also be refreshing to see them and compare them with WordCamps.&lt;/p&gt;

&lt;p&gt;And this is something I would advise every developer - be like a tourist, visit new places and learn about different cultures. There is nothing more valuable than learning from others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is WordPress heading?
&lt;/h2&gt;

&lt;p&gt;It's pretty hard to say. We love to hide behind our market share because if we have 40% of the market, everything is great, right?&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%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fwp-future%2Fjavier-allegue-barros-c7b-exxpoie-unsplash-%25281%2529.jpg%2Fbd56019bcbdbeb4c30d4519bcb5c4b82.webp" 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%2Fmaciekpalmowski.dev%2Fimg%2Fcontainers%2Fassets%2Farts%2Fwp-future%2Fjavier-allegue-barros-c7b-exxpoie-unsplash-%25281%2529.jpg%2Fbd56019bcbdbeb4c30d4519bcb5c4b82.webp" width="800" height="400"&gt;&lt;/a&gt; &lt;strong&gt;Recently, Jean Galea&lt;/strong&gt; wrote a &lt;a href="https://jeangalea.com/beyond-wordpress/" rel="noopener noreferrer"&gt;fantastic article&lt;/a&gt; that sums up most of the biggest problems in the WordPress space. When we put everything in one place, we can see WordPress space as a bit dysfunctional.&lt;/p&gt;

&lt;p&gt;It's also worth mentioning that some companies, always seen as 100% WordPress, are looking for an additional income source. Just look at &lt;strong&gt;Yoast&lt;/strong&gt; with their &lt;a href="https://yoast.com/shopify/apps/yoast-seo/" rel="noopener noreferrer"&gt;Shopify plugin&lt;/a&gt; or &lt;strong&gt;Kinsta&lt;/strong&gt; with their &lt;a href="https://kinsta.com/application-hosting/" rel="noopener noreferrer"&gt;Application and Database hosting&lt;/a&gt;. I think that it's a very healthy approach. Such an approach gives them much more stability in case the WordPress market changes.&lt;/p&gt;

&lt;p&gt;On the other hand, from the developer's perspective, WordPress is often seen as either legacy or boring. This makes it more challenging to find new talents interested in contributing to WordPress. For sure many of them will pick something related to &lt;strong&gt;Laravel&lt;/strong&gt; or &lt;strong&gt;Jamstack&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What will I do in the future?
&lt;/h2&gt;

&lt;p&gt;As I mentioned, switching to DevRel and breaking out of the &lt;strong&gt;WordPress&lt;/strong&gt; bubble was one of the best things that happened to me. WordPress did several things really great, but it also made some mistakes. Looking at this with an open mind and from multiple perspectives is very interesting.&lt;/p&gt;

&lt;p&gt;That's why I see my future in DevRel. It gives me much freedom to observe the IT world without closing myself in a bubble again.&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Is going headless worth the fuss?</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Tue, 08 Nov 2022 09:00:41 +0000</pubDate>
      <link>https://dev.to/palmiak/is-going-headless-worth-the-fuss-4o8h</link>
      <guid>https://dev.to/palmiak/is-going-headless-worth-the-fuss-4o8h</guid>
      <description>&lt;p&gt;Headless is one of the buzzwords that we keep hearing around. Very often it's described as something that will change our world, make the birds sing, and let the sun shine.&lt;/p&gt;

&lt;p&gt;I was always a bit skeptical about &lt;strong&gt;headless&lt;/strong&gt;, and I disagreed with the most popular pros of such an approach. I always saw headless as a waste of time or an excuse to play with new shiny toys. But things change, and more and more, I'm leaning towards the headless approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I didn't become a fan of headless from day one?
&lt;/h2&gt;

&lt;p&gt;In the &lt;strong&gt;WordPress&lt;/strong&gt; world, the term "headless" became popular primarily thanks to &lt;strong&gt;GatsbyJS&lt;/strong&gt;. Suddenly everyone started converting their websites to Gatsby and tweeted about how fast they had become.&lt;/p&gt;

&lt;p&gt;The speed argument was the one that always &lt;strong&gt;annoyed me&lt;/strong&gt;. Some of those devs didn't understand that they just saved their WP site as static HTML. It's challenging to have a slowly working HTML. Especially since WP already had multiple solutions to convert a website to static or to cache it (which works almost the same). Also, I know how to optimize a website, so in my case, Gatsby &lt;strong&gt;always had a worse PageSpeed score&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rDzG2N0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://maciekpalmowski.dev/assets/ob1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rDzG2N0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://maciekpalmowski.dev/assets/ob1.gif" alt="" width="271" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another popular argument was the one about how unique components are. Of course, but &lt;code&gt;include&lt;/code&gt; in PHP works similarly (at some level).&lt;/p&gt;

&lt;p&gt;In the end, "headless" annoyed me as hell, but I knew it was here to stay.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed?
&lt;/h2&gt;

&lt;p&gt;First of all, &lt;strong&gt;Astro&lt;/strong&gt; happened. &lt;strong&gt;Astro&lt;/strong&gt; was the first Jamstack SSG I liked, and it helped me understand many concepts of Jamstack. It's simple and easy to learn.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;WordPress&lt;/strong&gt; is pushing more toward the low-code/no-code direction. This caused my bigger interest in what is happening outside of WordPress. That's also the reason why this blog is created with &lt;strong&gt;Statamic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And what's most important, I understood that headless can give us much more freedom. I like using WordPress as a CMS, but I don't like the direction in which the rest is going (block themes, Full Site Editing, etc.). Until some point, I tried not to care, but lately, I started to feel that I was fighting against the modern WordPress vision.&lt;/p&gt;

&lt;p&gt;I even had a talk about this with &lt;strong&gt;Filip Rakowski&lt;/strong&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/--3l_MeP3rs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Off goes the head
&lt;/h2&gt;

&lt;p&gt;Some would say - &lt;em&gt;just change the CMS&lt;/em&gt;. But CMS is something more than just a toy for developers. In many cases, CMS is the workplace for marketing teams. We can't just change it when we find a new one (even if the new one is technically superior). On the other hand - our clients don't care which CMS we use. The whole front-end experience is what matters to them.&lt;/p&gt;

&lt;p&gt;That's why going headless is an excellent way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep the CMS which our team loves&lt;/li&gt;
&lt;li&gt;separate the front-end&lt;/li&gt;
&lt;li&gt;and very often hide the rotten core under the layer of new paint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Headless&lt;/strong&gt; will also give us more flexibility in case of changing the technology underneath. We are no longer glued to a specific technology like in a monolith approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are tradeoffs
&lt;/h2&gt;

&lt;p&gt;Of course, &lt;strong&gt;headless&lt;/strong&gt; also has its tradeoffs. You aren't maintaining one codebase that does everything, but at least two (the CMS and the front-end). Also, there is a big chance that developing a headless website will be more expensive.&lt;/p&gt;

&lt;p&gt;And let's not forget that it will be a bit more complicated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How the front-end will know when to rebuild itself after publishing new content?&lt;/li&gt;
&lt;li&gt;How to orchestrate the builds and deployments?&lt;/li&gt;
&lt;li&gt;How to take care of live-previews&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, before going headless, think about if you really need it.&lt;/p&gt;

</description>
      <category>headless</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Implementing Astro live previews in headless Statamic</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Tue, 25 Oct 2022 18:34:53 +0000</pubDate>
      <link>https://dev.to/palmiak/implementing-astro-live-previews-in-headless-statamic-hoc</link>
      <guid>https://dev.to/palmiak/implementing-astro-live-previews-in-headless-statamic-hoc</guid>
      <description>&lt;p&gt;Using Astro with different headless CMSs is excellent and relatively easy to achieve. The only problem is dealing with the live previews.&lt;/p&gt;

&lt;p&gt;I already wrote an article about this for &lt;a href="https://buddy.works/guides/statamic-rest-api"&gt;Buddy&lt;/a&gt; on using headless Statamic. If you want to learn a thing or two about head there first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's a problem?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Astro,&lt;/strong&gt; by default, is a Static Site Generator, which means that every time we change something in our content, we must rebuild the whole website. And this would mean waiting for each change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Side Rendering to the rescue
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt; introduced SSR some time ago and using this, we can create previews (and many other cool things) without rebuilding everything. We'll just need one extra server where SSR will work (e.g., Netlify).&lt;/p&gt;

&lt;p&gt;Also, it's worth mentioning that we can use a similar method not only for Astro but also for other Jamstack frameworks that have the option to use SSR.&lt;/p&gt;

&lt;p&gt;Let's imagine that our page looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
const API_URL = 'YOUR_DOMAIN';
let post = {};

export async function getStaticPaths() {

    const res = await fetch( `${API_URL}/collections/pages/entries/` );
    const posts = res.json();
    return posts.data.map((post) =&amp;gt; {
        return {
            params: { slug: post.slug },
            props: { post } };
        }
    );
}

const {post} = Astro.props;
---
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;{post.title}&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;

    &amp;lt;body&amp;gt;
        &amp;lt;main&amp;gt;
            &amp;lt;article&amp;gt;
                &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
                &amp;lt;div set:html={post.content}&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;/article&amp;gt;
        &amp;lt;/main&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Simple, right?&lt;/p&gt;

&lt;p&gt;Let's add some simple &lt;strong&gt;SSR&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
const API_URL = `YOUR DOMAIN`;
//let's store is_preview as an environmental variable
const is_preview = import.meta.env.PREVIEW;
let post = {};

export async function getStaticPaths() {
    const res = await fetch( `${API_URL}/collections/pages/entries/` );
   const posts = res.json();
    return posts.data.map((post) =&amp;gt; {
        return {
            params: { slug: post.slug },
            props: { post } };
        }
    );
}

// if true - SSR
if ( is_preview ) {
    post = await fetch( `${API_URL}/collections/pages/entries/` + `${Astro.params.slug}` );
    const res = await post.json();
    post = await res.data;
} else {
    const {post} = Astro.props;
}
---

&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;{post.title}&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;

    &amp;lt;body&amp;gt;
        &amp;lt;main&amp;gt;
            &amp;lt;article&amp;gt;
                &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
                &amp;lt;div set:html={post.content}&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;/article&amp;gt;
        &amp;lt;/main&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's take a look at what happened here.&lt;/p&gt;

&lt;p&gt;First of all, we add &lt;code&gt;is_preview&lt;/code&gt;, thanks to setting it as true or false, we'll be able to populate data with correct values.&lt;/p&gt;

&lt;p&gt;Also, it's essential to know that &lt;code&gt;getStaticPaths&lt;/code&gt; is ignored when running in SSR mode.&lt;/p&gt;

&lt;p&gt;Looks ok? More or less - time to go to &lt;strong&gt;Statamic&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live previews in Statamic
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Statamic&lt;/strong&gt; has a cool feature that lets us change the URL of the live preview. Go to &lt;a href="https://statamic.dev/live-preview#preview-targets"&gt;this page&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;So, in our case, we should just set our link to our server with SSR enabled (for example, &lt;strong&gt;Netlify&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;It's also worth mentioning that &lt;strong&gt;Statamic&lt;/strong&gt; adds two extra parameters to the URL &lt;code&gt;live-preview&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt;. Thanks to them, &lt;strong&gt;Statamic&lt;/strong&gt; can deal with non-saved data.&lt;/p&gt;

&lt;p&gt;So, after connecting everything, we can see that... the data doesn't refresh.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UCcZCGGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://maciekpalmowski.dev/assets/arts/statamic-astro-previews/statamic-astro.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UCcZCGGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://maciekpalmowski.dev/assets/arts/statamic-astro-previews/statamic-astro.gif" alt="'A not working example of live previews" width="797" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why? Because we forgot about passing the &lt;code&gt;live-preview&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt; parameters. The fix is straightforward:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
const API_URL = 'YOUR DOMAIN';
const is_preview = import.meta.env.PREVIEW;
let post = {};

export async function getStaticPaths() {
    const res = await fetch( `${API_URL}/collections/pages/entries/` );
    const posts = res.json();
    return posts.data.map((post) =&amp;gt; {
        return {
            params: { slug: post.slug },
            props: { post } };
        }
    );
}

if ( is_preview ) {
    const url = new URL(Astro.request.url);
    const params = new URLSearchParams(url.search);

    post = await fetch( `${API_URL}/collections/pages/entries/` + `${Astro.params.slug}` + '?token=' + `${params.get('token')}` + '&amp;amp;live-preview=' + `${params.get('live-preview')}` );
    const res = await post.json();
    post = await res.data;
} else {
    const {post} = Astro.props;

}
---
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;{post.title}&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;

    &amp;lt;body&amp;gt;
        &amp;lt;main&amp;gt;
            &amp;lt;article&amp;gt;
                &amp;lt;h1 &amp;gt;{post.title}&amp;lt;/h1&amp;gt;
                &amp;lt;div set:html={post.content}&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;/article&amp;gt;
        &amp;lt;/main&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, everything should work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should the final setup look like?
&lt;/h2&gt;

&lt;p&gt;In my case, it looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Server with &lt;strong&gt;Statamic&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  One &lt;strong&gt;Netlify&lt;/strong&gt; account where I have the final &lt;strong&gt;static&lt;/strong&gt; version of the website&lt;/li&gt;
&lt;li&gt;  One more free &lt;strong&gt;Netlify&lt;/strong&gt; account where I have the &lt;strong&gt;SSR&lt;/strong&gt; version &lt;/li&gt;
&lt;li&gt;  And, of course, &lt;strong&gt;Buddy&lt;/strong&gt; to automate all of this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Did you liked the article?
&lt;/h2&gt;

&lt;p&gt;If you liked the article and would like to read more - &lt;a href="http://newsletter.maciekpalmowski.dev/add_subscriber"&gt;Subscribe to my newsletter&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>astro</category>
      <category>statamic</category>
      <category>php</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Easiest way for Gutenberg Blocks with Timber and ACF Blocks</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Thu, 06 Feb 2020 09:59:23 +0000</pubDate>
      <link>https://dev.to/palmiak/easiest-way-for-gutenberg-blocks-with-timber-and-acf-blocks-23h7</link>
      <guid>https://dev.to/palmiak/easiest-way-for-gutenberg-blocks-with-timber-and-acf-blocks-23h7</guid>
      <description>&lt;p&gt;Some time ago I wrote the official &lt;a href="https://timber.github.io/docs/guides/gutenberg/"&gt;Timber Documentation on ACF Blocks&lt;/a&gt;. Now I’ll show you how to do it in a much easier manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparations
&lt;/h2&gt;

&lt;p&gt;Before we start we need to do three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Advanced Custom Fields PRO&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://github.com/palmiak/timber-acf-wp-blocks"&gt;Timber ACF WP Blocks&lt;/a&gt; library&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;views/blocks&lt;/strong&gt; folder in your theme&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best way to install the mentioned library is of course via Composer.&lt;/p&gt;

&lt;p&gt;In case you don’t like the &lt;strong&gt;views/blocks&lt;/strong&gt; folder you can change it by using &lt;code&gt;timber/acf-gutenberg-blocks-templates&lt;/code&gt; filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to create a block
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;views/blocks&lt;/code&gt; folder we create a twig file (in this example — &lt;code&gt;owl-link.twig&lt;/code&gt;) and on top of it, we add some additional comments. This Comment block is very important because thanks to it the block is registered and the values go to &lt;code&gt;acf_register_block_type&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="c"&gt;{#
  Title: Owl link
  Description: Link block
  Category: formatting
  Icon: admin-comments
  Keywords: link links
  Mode: edit
  Align: left
  PostTypes: page post
  SupportsAlign: left right
  SupportsMode: false
  SupportsMultiple: true
#}&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gutenberg-wordpressowka-block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gutenberg-url-block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fields.opis&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fields.link.url&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;fields.link.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, we can use all the parameters specified in the official &lt;a href="https://www.advancedcustomfields.com/resources/acf_register_block_type/"&gt;ACF Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The only thing that missing is setting one thing in ACF itself:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ty5o8hn4adfqhjpaa6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ty5o8hn4adfqhjpaa6h.png" alt="Alt Text" width="800" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s use it
&lt;/h2&gt;

&lt;p&gt;After registering and connecting our block there is one thing left — let’s write something:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxohz1o7xq8okkowi93zl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxohz1o7xq8okkowi93zl.png" alt="Alt Text" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s sum up everything
&lt;/h2&gt;

&lt;p&gt;As you see this is a very short article. Using &lt;a href="https://github.com/palmiak/timber-acf-wp-blocks"&gt;Timber ACF WP Blocks&lt;/a&gt; lets you focus on blocks itself instead of creating boring code :)&lt;/p&gt;

&lt;p&gt;If you would like to use this solution for Blade and Sage 9 use this &lt;a href="https://github.com/MWDelaney/sage-acf-wp-blocks"&gt;plugin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>timber</category>
      <category>wordpress</category>
      <category>gutenberg</category>
    </item>
    <item>
      <title>Timber - 4 reasons to love it</title>
      <dc:creator>Maciek Palmowski</dc:creator>
      <pubDate>Thu, 23 Jan 2020 23:10:21 +0000</pubDate>
      <link>https://dev.to/palmiak/timber-4-reasons-to-love-it-am3</link>
      <guid>https://dev.to/palmiak/timber-4-reasons-to-love-it-am3</guid>
      <description>&lt;p&gt;More than a year ago - Timber has become one of the basic tools I use in every WordPress based project. Here I'll try to show you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timber - what is it?
&lt;/h2&gt;

&lt;p&gt;The most basic definition would be that it's just Twig for WordPress. But that's not entirely true. Timber also adds many cool features like integrations with plugins (ACF, co-authors or WooCommerce) and filters for easy image resizing and manipulation.&lt;br&gt;
At this moment it's available as &lt;a href="https://pl.wordpress.org/plugins/timber-library/"&gt;plugin&lt;/a&gt; or &lt;a href="https://github.com/timber/timber"&gt;library&lt;/a&gt;, but soon we'll drop the plugin support.&lt;/p&gt;
&lt;h2&gt;
  
  
  It brings order to your code
&lt;/h2&gt;

&lt;p&gt;When you use Timber you have to separate logic from the view. Thanks to this we're avoiding WordPress's spaghetti coding like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'posts_per_page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'post_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'post'&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;div id="posts"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$wpisy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup_postdata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;p&amp;gt;&amp;lt;a href="'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;get_the_permalink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_the_title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'posts_per_page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'post_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'page'&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$pages&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;div id="strony"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$pages&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup_postdata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;p&amp;gt;&amp;lt;a href="'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;get_the_permalink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'"&amp;gt;'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_the_title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, we can be sure that in real life this code would be much more complicated and less readable.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Timber&lt;/strong&gt; it would look like this:&lt;br&gt;
&lt;strong&gt;archive.php&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_context&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'posts_per_page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'post_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'post'&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'posts_per_page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'post_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'page'&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pages'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'views/archive.twig'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;archive.twig&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;post.link&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;post.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;pages&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pages"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;pages&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;post.link&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;post.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As you see - it's much easier to read. Also, it's easier in terms of using brackets and curly brackets. I always had some problems in PHP + HTML in some situations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y2fFPKks--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/xwqpn97qt1yvl3h92zdo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y2fFPKks--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/xwqpn97qt1yvl3h92zdo.png" alt="Alt Text" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It has a great Advanced Custom Fields Integration
&lt;/h2&gt;

&lt;p&gt;I love ACF with all my heart but with &lt;strong&gt;Timber&lt;/strong&gt;, I loved it much more:&lt;/p&gt;

&lt;p&gt;In every example, the &lt;strong&gt;Timber&lt;/strong&gt; version is a bit shorter and more readable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;// Normal input
// without Timber
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;the_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'nazwa'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

// with Timber
{{ post.meta('nazwa') }}

// Image
// without Timber
$img = wp_get_attachment_image_src( get_field( 'image' ), 'large' );
echo $img[0];

// without Timber
{{ Image( post.image ).src( 'large' ) }}

// Repeater
// without Timber
if( have_rows('repeater_field_name') ):
    while ( have_rows('repeater_field_name') ) : 
        the_row();
        the_sub_field('sub_field_name');
    endwhile;
endif;

// with Timber
{% if post.meta( 'repeater_field_name' ) %}
  {% for field in post.meta( 'repeater_field_name' ) %}
    {{ field.sub_field_name }}
  {% endfor %}
{% endif %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Images
&lt;/h2&gt;

&lt;p&gt;Earlier I mentioned about image filters for resizing and manipulation. Now it's time to take a closer look at them.&lt;br&gt;
First of all - resizing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;post.image&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;| &lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;400&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this one line we get a 400x400px image.&lt;br&gt;
We can of course use image sizes defined by WordPress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;post.image&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'medium'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we would like to generate few images for srcset we can do this with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ Image( post.image ).src | resize( 400, 400 ) }}"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"{{ Image( post.image ).src | resize( 400, 400 ) }} 1x,
 {{ Image( post.image ).src | resize( 800, 800 ) }} 2x"&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;If you would like to learn more - check the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Twig Filters
&lt;/h2&gt;

&lt;p&gt;What I really hate about PHP is the moment when I have to use multiple functions on one variable. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, we want to remove spaces with trim()&lt;/li&gt;
&lt;li&gt;next change '-' to '_' with &lt;code&gt;str_replace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;and make a hash of it with md5&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can do it in two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' some variable'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// multiple bracket version&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// multiple line version&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most cases I forget about some bracket or it takes three lines. With Timber, it's much easier. First I have to create one extra filter thought:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// functions.php&lt;/span&gt;
&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'timber/twig'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'add_to_twig'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add_to_twig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$twig&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$twig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\Twig\TwigFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'md5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$twig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

// our phrase
{{ foo | trim | replace({ ' ', '-' }) | md5 }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I hope that those examples have shown how much &lt;strong&gt;Timber&lt;/strong&gt; can simplify the WordPress code. Personally, it really made me like WordPress even more. I hope you'll give &lt;a href="https://github.com/timber/timber"&gt;Timber&lt;/a&gt; a try.&lt;/p&gt;

&lt;p&gt;Have you tried Timber or any other templating system (for example Blade)?&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>twig</category>
    </item>
  </channel>
</rss>
