> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Type MIME et renderer Markdown dans Rails 8.1

> Rails 8.1 ajoute un type MIME et un renderer Markdown, afin qu'un contrôleur puisse répondre avec text/markdown. Cela ne convertit pas le Markdown en HTML, et cet article explique ce qu'il fait à la place.

export const YouTube = ({id, title, description, presenter, duration, republished}) => {
  const playlistId = "PLFHQSOKTqHXA";
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  if (!id || id === "YOUTUBE_ID") {
    return <div style={{
      border: "1px dashed currentColor",
      borderRadius: "0.5rem",
      opacity: 0.7,
      padding: "2rem 1.5rem",
      textAlign: "center",
      fontSize: "0.875rem"
    }}>
        <strong>Video not published yet.</strong>
        <br />
        {title ? `"${title}" has no YouTube id.` : "This tutorial has no YouTube id."}{" "}
        Replace <code>YOUTUBE_ID</code> in this page with the id from the
        video's URL, once it is in the{" "}
        <a href={playlistUrl}>GoRails x AppSignal playlist</a>.
      </div>;
  }
  const prettyDate = republished ? new Date(`${republished}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  }) : null;
  const schema = {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    name: title,
    embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,
    contentUrl: `https://www.youtube.com/watch?v=${id}`,
    thumbnailUrl: [`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`],
    creator: {
      "@type": "Organization",
      name: "GoRails",
      url: "https://gorails.com"
    },
    publisher: {
      "@type": "Organization",
      name: "AppSignal",
      url: "https://appsignal.com"
    }
  };
  if (description) schema.description = description;
  if (duration) schema.duration = duration;
  if (republished) schema.uploadDate = republished;
  if (presenter) schema.author = {
    "@type": "Person",
    name: presenter
  };
  return <div style={{
    marginBottom: "1.5rem"
  }}>
      <iframe width="100%" height="450" src={`https://www.youtube-nocookie.com/embed/${id}?list=${playlistId}`} title={title} style={{
    borderRadius: "0.5rem",
    border: 0,
    display: "block"
  }} allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />
      <p style={{
    fontSize: "0.875rem",
    marginTop: "0.5rem"
  }}>
        <a href={`https://www.youtube.com/watch?v=${id}&list=${playlistId}`}>{`Watch "${title}" on YouTube`}</a>
      </p>
      <script type="application/ld+json" dangerouslySetInnerHTML={{
    __html: JSON.stringify(schema)
  }} />
    </div>;
};

<YouTube id="L1ETcZickjA" title="Répondre avec du Markdown dans Rails 8.1" description="Rails 8.1 ajoute un type MIME et un renderer Markdown, afin qu'un contrôleur puisse répondre avec text/markdown. Cela ne convertit pas le Markdown en HTML, et cette vidéo explique ce qu'il fait à la place." presenter="Chris Oliver" duration="PT5M5S" republished="2026-09-19" />

Présenté par Chris Oliver pour [GoRails](https://gorails.com). Republié sur AppSignal le 19 septembre 2026.

Rails 8.1 ajoute un type MIME et un renderer Markdown. Cela a été largement mal compris à sa sortie, il vaut donc la peine de dire clairement ce que ce n'est pas : cela ne convertit pas le Markdown en HTML. Aucun parseur Markdown n'a été ajouté à Rails.

Cela permet à un contrôleur de répondre avec `text/markdown`, de la même façon qu'il peut déjà répondre avec du JSON.

<h2 id="what-this-covers">
  Ce que ce tutoriel couvre
</h2>

* Répondre à une requête `.md` avec `text/markdown`.
* La méthode `to_markdown` que le renderer attend sur votre objet.
* Le rendu du Markdown en HTML, qui est une tâche distincte pour une gem distincte.

<h2 id="requirements">
  Prérequis
</h2>

| Quoi  | Valeur                                                  |
| ----- | ------------------------------------------------------- |
| Rails | 8.1 ou supérieur, pour le type MIME et le renderer      |
| Gem   | `commonmarker`, uniquement si vous voulez aussi du HTML |

<h2 id="respond-with-markdown">
  Répondre avec du Markdown
</h2>

Ajoutez un format `md` au bloc `respond_to` de l'action :

```ruby theme={null}
def show
  respond_to do |format|
    format.html
    format.json { render json: @post }
    format.md { render markdown: @post }
  end
end
```

`format.md` correspond à une requête pour l'extension `.md`. `render markdown:` est le nouveau renderer.

<h2 id="define-to_markdown">
  Définir to\_markdown
</h2>

Le renderer appelle `to_markdown` sur ce que vous lui donnez. Sans cette méthode, la requête échoue avec `undefined method 'to_markdown'`.

Lorsque le Markdown se trouve déjà dans un attribut, déléguez-y :

```ruby theme={null}
class Post < ApplicationRecord
  delegate :to_markdown, to: :body
end
```

Une requête vers `/posts/3.md` renvoie maintenant le Markdown brut, avec un `Content-Type` de `text/markdown; charset=utf-8`.

Cet en-tête est tout l'intérêt de la fonctionnalité. Tout client qui négocie le contenu obtient désormais du Markdown depuis votre application comme une représentation à part entière, plutôt que du HTML qu'il doit nettoyer.

<h2 id="rendering-markdown-as-html">
  Rendre le Markdown en HTML
</h2>

Pour afficher du Markdown à une personne dans un navigateur, vous avez toujours besoin d'un parseur Markdown. `commonmarker` est un choix par défaut raisonnable, et prend en charge le Markdown au format GitHub.

```ruby theme={null}
# Gemfile
gem "commonmarker"
```

```erb theme={null}
<%= Commonmarker.to_html(@post.body).html_safe %>
```

<Warning>
  `html_safe` désactive l'échappement pour cette chaîne. Ne l'appelez que sur du Markdown auquel vous faites confiance, ou configurez le parseur pour qu'il assainisse les entrées non fiables. Du Markdown soumis par un utilisateur, rendu avec `html_safe` sans assainissement, constitue une faille de cross-site scripting.
</Warning>

<h2 id="why-this-shipped">
  Pourquoi cette fonctionnalité existe
</h2>

La motivation, ce sont les modèles de langage. Le Markdown est le format vers lequel les documents sont convertis avant d'être transmis à un modèle comme contexte. Une application qui fournit directement du `text/markdown` évite à chaque client de devoir reparser du HTML pour en tirer quelque chose d'utilisable.

C'est pour la même raison que la documentation d'AppSignal est disponible en Markdown. Chaque page ici est servie en Markdown brut à son URL avec une extension `.md`, et indexée dans [`llms.txt`](https://docs.appsignal.com/llms.txt). Un agent qui lit cette documentation obtient le texte plutôt que les éléments d'habillage de la page. Ajouter `format.md` à votre propre application la rend lisible pour les agents de la même façon.

Pour donner à un agent accès à vos propres données de monitoring, consultez [AppSignal pour les agents IA](/agents).

<h2 id="monitoring-markdown-responses">
  Surveiller les réponses Markdown
</h2>

Une fois qu'un endpoint sert du Markdown, deux choses méritent d'être surveillées.

* **Coût du rendu.** Convertir du Markdown en HTML à chaque requête est un vrai travail, qui a lieu à l'intérieur de la vue. Les [traces de performance](/performance-tracing) d'AppSignal découpent une requête en événements, si bien qu'un rendu lent apparaît comme son propre segment plutôt que comme du temps inexpliqué. Si le parsing s'avère significatif, mettez en cache le HTML plutôt que le Markdown.
* **Trafic des agents.** Les endpoints Markdown sont généralement sollicités par des outils plutôt que par des navigateurs, et leur profil de trafic est différent : par rafales, et souvent très répétitif. Donner au format son propre [nom d'action](/guides/actions) ou son propre [tag](/guides/tagging) permet de distinguer ce trafic de vos requêtes HTML, afin qu'aucun des deux ne fausse les temps de réponse de l'autre.

<h2 id="transcript">
  Transcription
</h2>

Transcrit à partir de la vidéo et légèrement édité : les sous-titres automatiques ont mal compris plusieurs noms de produits et d'API, qui ont été corrigés.

<Accordion title="Lire la transcription">
  **0:02** Salut à tous, dans cet épisode, on parle du nouveau type MIME et renderer Markdown de Rails 8.1, de son fonctionnement et de son utilisation dans vos applications Rails. J'ai ici une application Rails toute simple avec un scaffold pour des posts. Ils ont un titre, ils ont un corps. On va écrire quelque chose comme Hello World, et on va utiliser du Markdown ici en écrivant par exemple GoRails, https, gorails.com, is awesome. On va écrire un peu de Markdown là-dedans, et on va le faire rendre.

  **0:35** Mais ce n'est qu'une chaîne de caractères, le Markdown n'est que du texte, et on doit pouvoir l'afficher dans l'interface avec du vrai HTML. Cette nouvelle fonctionnalité de Rails 8.1 ne sert pas à faire du rendu en HTML. Elle sert en réalité à rendre le Markdown comme type MIME, et comme type de réponse dans vos vues. Donc pour l'afficher correctement, on va utiliser une gem appelée Commonmarker, qui peut prendre votre texte Markdown, le convertir en HTML, et l'afficher. Je l'ai déjà installée, mais vous pouvez l'ajouter à votre Gemfile et lancer bundle pour l'installer.

  **1:14** Ensuite, on va dans notre vue, à l'endroit où le corps du post est affiché, et on écrit Commonmarker.to\_html, on lui donne ce contenu, puis on peut le marquer comme html\_safe ensuite. Il y a aussi tout un tas d'options que vous pouvez ajouter, que vous pouvez consulter dans la documentation de Commonmarker, mais on va utiliser les valeurs par défaut ici. On actualise notre page, et on obtient maintenant GoRails is awesome, où GoRails est le lien issu du Markdown, et awesome est en italique, parce que le Markdown a converti ça en balise ancre et en balise EM. C'est top. Ça fonctionne comme on s'y attendait. Maintenant, la fonctionnalité de Rails 8.1 du Markdown comme type MIME et comme renderer fait en fait référence au renderer dans vos contrôleurs.

  **2:02** Donc quand on a une action comme celle-ci où on peut répondre en HTML, ou en version JSON, on peut maintenant écrire format.md. Ça va chercher l'extension de fichier .md dans l'URL, et ensuite on peut écrire render markdown, qui est le renderer Markdown. Ce .md est donc le raccourci pour ce type MIME. On a ensuite un render markdown, et on peut lui donner le post. Et si on essaie ça dans le navigateur et qu'on visite 3.md, ça va nous donner une erreur interne du serveur, et ce sera un appel de méthode non définie pour une instance de post.

  **2:41** Ce qui se passe ici, c'est essentiellement que le renderer Markdown va essayer d'appeler to\_markdown sur notre modèle, et on peut ensuite déléguer ça à l'attribut body, où se trouve notre texte Markdown. Et si on actualise dans le navigateur, on obtient le contenu Markdown brut, comme attendu. Et si on ouvre l'onglet réseau du navigateur et qu'on actualise, on voit cette requête, et le content type va être text/markdown avec un jeu de caractères UTF-8. C'est donc le type MIME qui est utilisé pour le content type de notre réponse. Et c'est utile à l'époque de l'IA : comme le mentionne DHH, le Markdown est ce qu'on utilise pour le donner à nos LLM afin qu'ils le lisent et l'utilisent comme contexte dans votre requête.

  **3:32** C'est donc très utile pour ça, mais ça ne fait pas vraiment de rendu du Markdown en HTML. Et c'est là qu'un outil comme Commonmarker entre en jeu. C'est ce que j'utilise désormais pour à peu près tout. Il a tout un tas d'options, beaucoup des mêmes fonctionnalités de Markdown au format GitHub, et ça fonctionne vraiment bien. Et c'est ce qu'on peut utiliser pour l'afficher réellement en HTML dans le navigateur.

  **3:59** Mais maintenant, on a le type MIME format.md enregistré, ainsi que le renderer Markdown. Et tout ce qu'on a à faire, c'est définir cette méthode to\_markdown. Et c'est bon. On peut donc faire répondre n'importe laquelle de nos vues avec du Markdown comme content type. C'est plutôt sympa.

  **4:20** C'est vraiment tout ce qu'il y a à savoir. Ce n'est pas une grosse fonctionnalité. Je sais que beaucoup de gens étaient super enthousiastes à ce sujet. Je pense que beaucoup ont supposé que ce serait quelque chose comme Commonmarker ajouté à Rails, mais ce n'est pas le cas : c'est en fait seulement le content type, le type MIME et le renderer, et c'est là, je pense, la partie qui prête à confusion.

  **4:37** Le renderer veut seulement dire que le helper render comprend que oui, ce type MIME doit aller récupérer et renvoyer ce contenu. C'est donc vraiment tout ce qu'il y a à savoir. Il n'y a pas grand-chose d'autre à dire ici, mais c'est tout ce que vous devez faire dans vos modèles pour qu'ils soient rendus en Markdown. C'est plutôt sympa.
</Accordion>

<h2 id="related-tutorials">
  Tutoriels associés
</h2>

* [Local CI](/tutorials/ruby/local-ci)
* [params.expect](/tutorials/ruby/params-expect)

<h2 id="about-this-tutorial">
  À propos de ce tutoriel
</h2>

Ce tutoriel résume un screencast GoRails de Chris Oliver sur le type MIME et le renderer Markdown de Rails 8.1. Le screencast est l'œuvre originale. GoRails le publie, ainsi que le reste de la série, sur [gorails.com](https://gorails.com).
