<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tomasz Kikowski</title>
    <description>The latest articles on Forem by Tomasz Kikowski (@qiqqq).</description>
    <link>https://forem.com/qiqqq</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%2F1037824%2F66b0d85d-3973-4261-afc9-6dd9ebee487e.jpeg</url>
      <title>Forem: Tomasz Kikowski</title>
      <link>https://forem.com/qiqqq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/qiqqq"/>
    <language>en</language>
    <item>
      <title>Create minimalist blog with Nuxt 3 and Tailwind CSS</title>
      <dc:creator>Tomasz Kikowski</dc:creator>
      <pubDate>Mon, 27 Mar 2023 11:34:50 +0000</pubDate>
      <link>https://forem.com/qiqqq/create-minimalist-blog-with-nuxt-3-and-tailwind-css-1dm</link>
      <guid>https://forem.com/qiqqq/create-minimalist-blog-with-nuxt-3-and-tailwind-css-1dm</guid>
      <description>&lt;p&gt;Nuxt 3 is one of the hottest tools in web development right now, and for good reason. This powerful tool makes it easy to create any web application you wish. In this guide, I will show you how to create a minimalistic blog with Nuxt 3 as a base, Tailwind CSS for styling user interface, and Markdown to create posts. Let's dive in and create a stunning blog together!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You have to install the following dependencies at the beginning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; (latest LTS version or above 16.11)&lt;/li&gt;
&lt;li&gt;Some code editor, for example &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a new Nuxt 3 application
&lt;/h2&gt;

&lt;p&gt;You need to start this journey by creating a new Nuxt 3 project. To do this run the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nuxi@latest init nuxt3-blog-app
cd nuxt3-blog-app
npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Side note: If you are not familiar with NPX you can jump to this article about the &lt;a href="https://nerdanswers.com/npx-vs-npm-what-are-differences/"&gt;differences between NPX and NPM&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By running the above command you will create Nuxt 3 application in &lt;code&gt;nuxt3-blog-app&lt;/code&gt; directory, then you will go to this directory and install required dependencies. After this, you can run this project by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing Tailwind CSS
&lt;/h3&gt;

&lt;p&gt;The next step you should do is to install Tailwind CSS together with Post CSS and Autoprefixer. After successful installation, run the init command to generate &lt;code&gt;tailwind.config.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the generated configuration file you need to specify paths to all components, and any other source files that will contain Tailwind class names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./components/**/*.{js,vue,ts}",
    "./layouts/**/*.vue",
    "./pages/**/*.vue",
    "./plugins/**/*.{js,ts}",
    "./nuxt.config.{js,ts}",
    "./app.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you should create the main CSS file (for example &lt;code&gt;/assets/css/main.css&lt;/code&gt;) for all Tailwind CSS rules with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally you have to add PostCSS configuration and include the newly created CSS file in &lt;code&gt;nuxt.config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineNuxtConfig({
  css: ['~/assets/css/main.css'],
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing @nuxt/content module
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@nuxt/content&lt;/code&gt; module (&lt;a href="https://content.nuxtjs.org"&gt;https://content.nuxtjs.org&lt;/a&gt;) is a great utility that helps you to manage content on your blog. You can use following command to install this module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D @nuxt/content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to enable &lt;code&gt;@nuxt/content&lt;/code&gt; module by adding it to &lt;code&gt;nuxt.config.js&lt;/code&gt; configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineNuxtConfig({
  modules: ['@nuxt/content'],
  ...
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating and displaying content
&lt;/h2&gt;

&lt;p&gt;To store posts on the blog you can use Markdown files. To do this create a separate directory to store them (for example &lt;code&gt;/content&lt;/code&gt;) and add some Markdown files with content and metadata. Add minimum four different blog posts for best look  of the blog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir content
touch first-example-blog-post.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example structure of such a blog post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
title: "Lorem ipsum dolor sit amet"
author: John Doe
avatar: https://i.pravatar.cc/40
date: Mar 25, 2023
image: https://picsum.photos/1200/520
excerpt: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi at mi suscipit sapien volutpat volutpat. Nam tempor malesuada ligula, et pretium ante ultricies sed.
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
Morbi at mi suscipit sapien volutpat volutpat. Nam tempor malesuada ligula, et pretium ante ultricies sed. 

Cras condimentum ex a facilisis venenatis. Suspendisse congue fringilla risus. Nullam faucibus tincidunt sagittis. Proin vitae aliquet lacus, ac elementum mauris.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating pages
&lt;/h3&gt;

&lt;p&gt;You need two types of pages for blog - main blog page for displaying list of all post and single post page. To do this, create &lt;code&gt;index.vue&lt;/code&gt; and &lt;code&gt;[[slug]].vue&lt;/code&gt; files in &lt;code&gt;/pages&lt;/code&gt;. directory and fill them by some dummy template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;Blog page&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, edit &lt;code&gt;app.vue&lt;/code&gt; file in the main directory and change it by adding &lt;code&gt;NuxtPage&lt;/code&gt; component (thanks to this Nuxt will read pages inside &lt;code&gt;/pages&lt;/code&gt; directory and create routes form them) and some Tailwind CSS classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;main class="max-w-screen-xl mx-auto px-4 sm:px-6 xl:px-10 py-5 xl:py-10 text-base"&amp;gt;
    &amp;lt;NuxtPage /&amp;gt;
  &amp;lt;/main&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Displaying content on the main blog page
&lt;/h3&gt;

&lt;p&gt;To display content on main blog page, you need to create some post tile component. Go to &lt;code&gt;/components&lt;/code&gt; directory, make a new file - &lt;code&gt;PostTile.vue&lt;/code&gt; with following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div 
    class="pb-8 lg:pb-12"
    :class="{ 'w-full md:flex': isBig, 'md:w-1/3': !isBig }"
  &amp;gt;
    &amp;lt;div
      class="px-4 lg:px-6"
      :class="{ 'md:w-2/3': isBig }"
    &amp;gt;
      &amp;lt;a :href="post._path" :title="post.title"&amp;gt;
        &amp;lt;img :src="post.image" :alt="post.title" class="rounded block"&amp;gt;
      &amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div
      class="px-4 lg:px-6"
      :class="{ 'md:w-1/3': isBig }"
    &amp;gt;
      &amp;lt;h3 class="my-3" :class="{ 'md:mt-0 md:mb-6': isBig }"&amp;gt;
        &amp;lt;a :href="post._path" :title="post.title"&amp;gt;{{ post.title }}&amp;lt;/a&amp;gt;
      &amp;lt;/h3&amp;gt;
      &amp;lt;div class="font-serif text-lg"&amp;gt;
        &amp;lt;p
          class="my-3"
          :class="{ 'md:my-6': isBig }"
        &amp;gt;
          {{ post.excerpt }}
        &amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="flex items-center"&amp;gt;
        &amp;lt;img :src="post.avatar" :alt="post.author" class="w-12 h-12 rounded-full"&amp;gt;
        &amp;lt;div class="ml-4"&amp;gt;
          &amp;lt;strong class="block"&amp;gt;{{ post.author }}&amp;lt;/strong&amp;gt;
          &amp;lt;span&amp;gt;{{ post.date }}&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  name: 'PostTile',
  props: {
    post: {
      type: Object,
      default: () =&amp;gt; {}
    },
    // First blog post will be displayed as big tile
    isBig: {
      type: Boolean,
      default: false
    }
  },
})
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next open &lt;code&gt;pages/index.vue&lt;/code&gt; file, get all posts and display them using created &lt;code&gt;PostTile.vue&lt;/code&gt; component (Nuxt 3 automatically import all components in &lt;code&gt;/components&lt;/code&gt; directory so you can just use it in the template):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class="flex flex-wrap -mx-4 lg:-mx-6"&amp;gt;
    &amp;lt;PostTile
      v-for="(post, key) in posts"
      :key="post.title"
      :post="post"
      :is-big="!key"
    /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  async setup() {
    const { data:posts } = await useAsyncData('posts', () =&amp;gt; queryContent('/').find())

    return {
      posts
    }
  },
})
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Displaying content on the single post page
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@nuxt/content&lt;/code&gt; module will automatically detect Markdown files and create routes based on the file name. The only one thing you need to do is edit &lt;code&gt;/pages/[[slug]].vue&lt;/code&gt; file and create appropriate template to display post content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;article&amp;gt;
    &amp;lt;h1 class="my-6 text-center font-serif"&amp;gt;
      {{ post.title }}
    &amp;lt;/h1&amp;gt;
    &amp;lt;div class="my-8 text-center font-serif text-base text-gray"&amp;gt;
      &amp;lt;time&amp;gt;{{ post.date }}&amp;lt;/time&amp;gt; &amp;amp;mdash; {{ post.author }}
    &amp;lt;/div&amp;gt;
    &amp;lt;img
      :src="post.image"
      :alt="post.title"
      class="rounded block my-10"
    /&amp;gt;
    &amp;lt;div class="max-w-3xl mx-auto my-8"&amp;gt;
      &amp;lt;ContentRenderer 
        class="content font-serif text-lg"
        :value="post" 
      /&amp;gt;
      &amp;lt;div class="sm:flex justify-between items-center mt-16"&amp;gt;
        &amp;lt;div class="flex items-center"&amp;gt;
          &amp;lt;img :src="post.avatar" :alt="post.author" class="w-12 h-12 rounded-full"&amp;gt;
          &amp;lt;div class="ml-4"&amp;gt;
            &amp;lt;strong class="block"&amp;gt;{{ post.author }}&amp;lt;/strong&amp;gt;
            &amp;lt;span&amp;gt;{{ post.date }}&amp;lt;/span&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { defineComponent } from '@vue/composition-api';
import { useRoute } from "vue-router";

export default defineComponent({
  async setup() {
    const route = useRoute()
    const { data:post } = await useAsyncData('post', () =&amp;gt; queryContent(route.path).findOne())

    return {
      post
    }
  },
})
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code you may notice &lt;code&gt;ContentRenderer&lt;/code&gt; component. This component is built into &lt;code&gt;@nuxt/content&lt;/code&gt; module and you can use it to convert Markdown syntax to HTML code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling and adding header
&lt;/h3&gt;

&lt;p&gt;Finally, you need to little improve CSS styles to make this blog look better.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;nuxt.config.js&lt;/code&gt; file add configuration of links to use Google Fonts (in this case Lato &amp;amp; PT Sans):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineNuxtConfig({
  app: {
    head: {
      link: [
        { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
        { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
        { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Lato:wght@400;700&amp;amp;family=PT+Serif:wght@400;700&amp;amp;display=swap' }
      ]
    },
  },
  ...
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next add some additional styles in Tailwind CSS configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    fontFamily: {
      'sans': ['Lato', 'sans-serif'],
      'serif': ['PT Serif', 'serif']
    },
    extend: {
      colors: {
        black: '#393939',
      }
    }
  },
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add some additional styles in &lt;code&gt;/assets/main.css&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;

@layer base {
  h1 {
    @apply text-4xl font-bold;
  }
  h2 {
    @apply text-2xl font-bold;
  }
  h3 {
    @apply text-xl font-bold;
  }
  .content h1, 
  .content h2, 
  .content h3, 
  .content p, 
  .content img {
    @apply my-6;
  }
  .content ul {
    @apply my-6 pl-5 list-disc;
  }
  .content ol {
    @apply my-6 pl-5 list-decimal;
  }
  li {
    @apply my-4;
  }
  @media (min-width: 768px) {
    h1 {
      @apply text-6xl font-bold;
    }
    h2 {
      @apply text-4xl font-bold;
    }
  }
}

@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tailwind by default normalize all styles, for example by removing default font sizes of headings or margins of paragraphs, lists, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding header
&lt;/h3&gt;

&lt;p&gt;To allow navigate between single post and main blog page add header with link to the home page. Create a new file for header component - &lt;code&gt;/components/Header.vue&lt;/code&gt; and fill it with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div  class="text-center uppercase text-3xl py-6 mb-2 border-b border-gray-200"&amp;gt;
    &amp;lt;NuxtLink to="/"&amp;gt;
      &amp;lt;strong&amp;gt;Nuxt3&amp;lt;/strong&amp;gt;Blog
    &amp;lt;/NuxtLink&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include header in &lt;code&gt;app.vue&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;&amp;lt;template&amp;gt;
  &amp;lt;Header /&amp;gt;
  ...
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thats all. You've just created simple blog application with Nuxt 3 and Tailwind CSS with content stored in Markdown files.&lt;/p&gt;

&lt;p&gt;You can find above code in &lt;a href="https://github.com/qiqqq/nuxt3-blog-template"&gt;GitHub repository&lt;/a&gt; or check &lt;a href="https://nuxt3-blog-template.vercel.app/"&gt;live version on Vercel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>frontend</category>
      <category>tailwindcss</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
