Créez votre blog (presque) sans code avec Nuxt 3 et Nuxt/Content
Tags: vue, tutorial, webdev, french
Published: 2022-07-28 12:13:00

Pour certain·e·s, c'est le désir de partager, pour d'autres, c'est une façon de prendre des notes, mais on a tous·tes, en tant que développeur·se, au moins une bonne raison de créer notre propre dev blog. Sauf que bien des fois, on peut se retrouver découragé·e devant la myriade de technologies à notre disposition pour créer un site web, et la potentielle courbe d'apprentissage de celles-ci.

Et si je te dis que grâce à Nuxt 3 et au module Nuxt/Content, il devient beaucoup plus facile de créer un site dit "content-driven", comme le serait notre dev blog.

Nuxt 3 est le framework "piles incluses" de Vue 3. Il est, à l'heure où j'écris cet article, encore en Release Candidate, mais peu de choses devraient changer d'ici la version finale.
Le but de cet article n'est pas de faire une revue détaillée du fonctionnement de Nuxt puisque nous n'allons écrire que peu de code propre au framework mais je t'invite à jeter un oeil à [la documentation](https://v3.nuxtjs.org/) pour te permettre d'aller plus loin par la suite.

Nuxt/Content est un module Nuxt qui permet au framework de transformer des simples fichiers markdown en véritables pages pour nous permettre de créer un site en se concentrant sur le contenu. Il y a aussi [une documentation](https://content.nuxtjs.org/) pour t'aider à approfondir sur le module.

## Création du projet

Dans ton terminal préféré, il te suffit de lancer la commande suivante pour initialiser un projet Nuxt :
```sh
npx nuxi init mon-blog
```
Pour être tranquille pour la suite, nous allons installer ce dont on a besoin :
```sh
cd mon-blog
npm install
npm i -D @nuxt/content
```
Le dev server se lance avec la commande : 
```sh
npm run dev
```
En navigant sur http://localhost:3000, tu te retrouves alors sur la page de notre nouveau site, auto-générée par la CLI de Nuxt (nuxi).

Afin de pouvoir utiliser le module content, il faut l'ajouter aux modules dans la config Nuxt `nuxt.config.ts` comme ci-dessous :
```ts
import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({
  modules: ['@nuxt/content']
})
```

## Un peu de code

Désolé, il y aura quand même un peu de code... Il faut bien préparer la structure! 

### Une première page

La première chose à faire est de préparer les pages. Nuxt, par convention, transformera chaque composant Vue déclaré dans le dossier `pages` en page, dont le chemin sera le nom donné au fichier. Ainsi `pages/index.vue` va créer une route qui correspond à l'index de notre site. Et c'est ce que nous allons faire avec un placeholder: 
```vue
<!-- pages/index.vue -->
<template>
  <div>index page</div>
</template>
```

Ensuite, il nous faut soit retirer le fichier `app.vue` (et laisser Nuxt wrapper les pages lui même) ou l'éditer pour lui indiquer d'utiliser les pages (et layouts).

```vue
<!-- app.vue -->
<template>
  <div>
    <NuxtLayout>
      <NuxtPage/>
    </NuxtLayout>
  </div>
</template>
```

Si l'on essaie d'accéder à une quelconque adresse autre que l'index, Nuxt nous renverra une erreur 404. Ce qui est normal puisque nous n'avons configuré qu'une unique page.

### D'autres pages grâce au catch-all

Pour garder une structure simple dans le cadre de cet article, nous considérerons, que toutes les autres pages sont potentiellement des articles du blog. Pour les exposer, il suffit alors de créer une page dite "catch-all". Sous le dossier `pages/`, on appellera ce composant `[...slug].vue`, c'est la convention retenue par Nuxt pour "attraper" tout autre chemin qui n'est pas clairement défini comme une page (comme l'est `index.vue`).

```vue
<!-- pages/[...slug].vue -->
<template>
  <main>
    <ContentDoc />
  </main>
</template>
```

Et ça suffit pour le moment pour le code. Mais qu'est-ce que ce composant `ContentDoc` et d'où sort-il ? 

Il s'agit d'un composant auto-importé par Nuxt, exposé par le module Content qui a deux fonctionnalités:
- Récupérer le contenu d'un fichier markdown.
- Faire le rendu de celui-ci.

Où va-t-il récupérer le contenu ? C'est le sujet de la suite !

## Ajouter du contenu

Par convention, le module content ira chercher tout fichier markdown, yaml, json et même csv dans un dossier `content/` situé à la racine.

C'est donc parti pour écrire notre premier post en créant un fichier Markdown `content/mon-premier-post.md`.

```md
# Mon premier post

Ceci est le contenu du premier post de mon blog Nuxt + Content.
```

Et juste avec ça, nous venons de créer le premier post de notre blog! En navigant vers http://localhost:3000/mon-premier-post, on peut voir le contenu parsé et rendu en HTML équivalent à notre Markdown.

![Rendu de page web avec pour titre 'Mon premier post' et un paragraphe exemple](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2ekql0zu3921mepaz8s.png)

Pour donner un peu plus de contenu, nous allons créer un deuxième post avec `content/un-autre-post.md`.

```
# Un autre post

Et voici un autre post, le premier s'ennuyait.
```

Et une autre page est ajoutée à notre site : https://localhost:3000/un-autre-post

# Ajouter la navigation

Maintenant que nous avons quelque posts, il serait intéressant de pouvoir accéder à ceux-ci depuis notre page d'accueil. Nuxt/Content expose le composant 'ContentNavigation', il s'agit d'un composant dit "renderless": ce composant ne fait aucun rendu mais expose des variables et méthodes à ses composants enfants. Reprenons notre page `index.vue` comme ceci:

```vue
<template>
  <div>
    <h1>Mon blog Nuxt 3 + Content 2</h1>
    <nav>
      <ul>
        <ContentNavigation v-slot="{ navigation }">
          <li v-for="link of navigation" :key="link._path" >
            <NuxtLink :to="link._path">
              {{ link.title }}
            </NuxtLink>
          </li>
        </ContentNavigation>
      </ul>
    </nav>
  </div>
</template>
```

Le composant `ContentNavigation` génère une liste de liens vers chacune des pages que nous avons créées et les expose auto-magiquement au travers de `navigation`. Si cette mécanique Vue appelée "scoped slots" t'es inconnue, voici [la documentation](https://vuejs.org/guide/components/slots.html#scoped-slots).

# Allez plus loin

Maintenant que nous avons une structure, il est temps d'ajouter un peu d'âme et de customisation à notre blog.

## Modifier les composants Markdown (Prose)

Il est très facile de surcharger les composants utilisés par Nuxt/Content pour transformer le markdown en HTML. Voici la [liste des composants Prose](https://content.nuxtjs.org/api/components/prose) que Nuxt/Content met à notre disposition.

Ainsi, en déclarant un composant `ProseH1.vue` dans un dossier `components/content/` comme ceci: 

```vue
<!-- components/content/ProseH1.vue -->
<template>
  <h1><slot /></h1>
</template>

<style scoped>
h1 {
  color: red;
  font-weight: bold;
}
</style>
```

Tous les `h1` générés par les `#` markdown sont maintenant rouge et en gras, comme décidé dans notre composant `ProseH1` !

## La syntaxe MDC

Toujours dans l'optique de centrer la création des sites sur le contenu, le module Nuxt/Content apporte une syntaxe Markdown particulière : les MDC (ou MarkDown Components). Nous allons pouvoir créer des composants Vue et les réutiliser directement depuis nos fichiers de contenu en Markdown. Cette syntaxe va nous permettre d'utiliser les nombreuses API des composants Vue (props, slots...) depuis les fichiers Markdown.

Voici un bouton pour l'exemple, qu'on mettre lui aussi dans `components/content` : 

```vue
<!-- components/content/CustomButton -->
<script setup lang="ts">
defineProps<{ color: string }>()

const sendAlert = () => alert('Hello :)')
</script>

<template>
  <button @click="sendAlert" :style="{ color }">
    <slot />
  </button>
</template>
```

Allons éditer notre premier post pour y ajouter ce bouton :

```md
# Mon premier post

Ceci est le contenu du premier post de mon blog Nuxt + Content.

::CustomButton{color='red'}
clique sur moi!
::
```

On peut directement passer des props au composant, comme nous avons passé `color` dans cet exemple! Cette syntaxe supporte même les named slots. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ced6saaqyjjeaumcixao.png)

Je te laisse approfondir dans [la documentation](https://content.nuxtjs.org/guide/writing/mdc). Tout composant déclaré dans `components/content` sera accessible depuis les fichier Markdown de contenu, les possibilités sont presque infinies!

Cette article reste une introduction très basique à Nuxt et au module Content, mais j'espère qu'il te donne une idée du monde de possibilités que ces technologies offrent. A considérer aussi qu'à l'heure ou j'écris cet article, elles sont encore jeunes (`nuxt@3.0.0RC-6` et `@nuxt/content@v2.0.1`), et seront surement amenées à évoluer.

_Tu peux retrouver le code de cet article sur [ce repo](https://github.com/prazdevs/blog-nuxt-content-fr). Je t'invite aussi à passer sur [mon dev blog](https://praz.dev), réalisé avec Nuxt3 et Nuxt/Content, dont tu peux retrouver le code [ici](https://github.com/prazdevs/praz.dev)!_