Chargement…

Personnalisation de React-Markdown en Typescript

Personnalisation de React-Markdown en Typescript

Une meilleure association Strapi Next.js

Lorsque vous rédigez vos articles avec Strapi, un input de type RichText se contentera de générer du texte au format Markdown. Pour pouvoir l'afficher avec la mise en forme, côté Next.js, nous allons utiliser un composant React : React-Markdown.

A quoi sert cette librairie ?

Elle remplace le Markdown généré par Strapi en composants HTML plus traditionnel.

Des

<H1> <H2> <H3>

pour les titres,

des

<A> 

pour les liens,

des

<UL>

pour les listes...

Cette librairie est personnalisable à souhait. On peut remplacer pour chaque élément HTML rendu par un composant custom et la documentation nous montre comment nous y prendre :

import React from 'react'
import ReactDOM from 'react-dom'
import ReactMarkdown from 'react-markdown'
import MyFancyRule from './components/my-fancy-rule.js'

ReactDOM.render(
  <ReactMarkdown
    components={{
      // Use h2s instead of h1s
      h1: 'h2',
      // Use a component instead of hrs
      hr: ({node, ...props}) => <MyFancyRule {...props} />
    }}
  >
    # Your markdown here
  </ReactMarkdown>,
  document.querySelector('#content')
)

En JSX cela ne pose aucun problème, mais en TSX c'est une autre pair de manches...

Comment se fait la surcharge de React-Markdown

On passe un objet à la propriété components, et chaque clé de cet objet représente un composant. Le problème, c'est que le renderer est une fonction et cela pose pas mal de problème pour pouvoir typer ces éléments.

Voici comment j'ai procédé :

Je commence par créer mon composant MarkdownLink qui retourne un JSX.Element basique :

import * as React from 'react'

type MarkdownLinkProps = {
  href: string
  children: React.ReactNode
}

const MarkdownLink = ({ href, children }: MarkdownLinkProps) => {
  return (
    <a href={href} target="_blank" rel="noreferrer">
      {children}
    </a>
  )
}

export default MarkdownLink

Puis au moment du render de React-Markdown :

<ReactMarkdown
    components={{
        a: ({ node, children }) => {
           return (
               <MarkdownLink href={(node.properties?.href as string) ?? ''}>
                  {children[0]}
               </MarkdownLink>
            )
        },
    }}
   >
   {post.attributes.content}
</ReactMarkdown>

Et voilà, notre composant remplace désormais la balise standard généré par React-Markdown. 😉

FAQ

Pourquoi utiliser React-Markdown plutôt que d'afficher le Markdown brut directement ?

Le contenu Markdown brut n'est pas interprété par le navigateur, il s'affiche tel quel sans mise en forme. React-Markdown convertit automatiquement ce texte en balises HTML lisibles et structurées.

Pourquoi la surcharge des composants pose-t-elle problème en TypeScript ?

En JSX, le typage est souple et les fonctions de rendu passées à la prop components sont acceptées sans friction. En TypeScript, le compilateur exige un typage précis des paramètres de ces fonctions, ce qui nécessite une approche plus explicite.

Comment récupérer l'attribut href d'un lien dans un renderer personnalisé ?

Dans la fonction de rendu, on accède à node.properties?.href qui provient de l'arbre syntaxique interne. Un cast en string avec une valeur par défaut permet d'éviter les erreurs de typage.

Est-ce qu'on peut personnaliser d'autres balises que les liens avec cette approche ?

Oui, la prop components accepte une clé pour chaque élément HTML que React-Markdown peut générer, comme h1, ul, hr ou img. Il suffit de répéter le même principe pour chaque balise à surcharger.

Faut-il obligatoirement créer un composant séparé pour chaque élément personnalisé ?

Non, on peut écrire la logique directement dans la fonction inline passée à components. Créer un composant séparé est surtout utile quand le rendu est complexe ou réutilisé ailleurs dans le projet.


Poursuivre la lecture

Passage de React-Markdown à Marked Dev
#react-markdown#marked#blog

Passage de React-Markdown à Marked

Dernièrement, j'écrivais un article sur la personnalisation de React-Markdown. Cette librairie comporte beaucoup d'avantages, elle est pratique, simple, légère. Mais elle comporte aussi des inconvénients, elle est assez limitée en terme de possibilités. C'est pourquoi je passe à Marked...

Alexandre P.
Petit point technique sur le blog Dev
#blog#moteur#custom

Petit point technique sur le blog

Découvrez pourquoi j'ai migré mon blog d'un moteur custom Node.js Express/Handlebars vers une plateforme Next.js, Strapi et React en full TypeScript.

Alexandre P.