Generated Excerpts for Nuxt3 Content

Nuxt3 has been my stack of choice for a while now and it was time to port my site over from Nuxt2 — an exercise in itself I should blog about — but more concretely is the idea of excerpts.

Basically when you have a list of articles you want to show a short snippet of the article to entice the reader to click on it. This can either be the description if it’s a simple one-liner used in a card for example, or it can be the start of the article itself. Nuxt’s Content v2 supports generating excerpts for you by taking the content up to the <!--more--> marker in the text as the excerpt.

This is great if you’ve done it but if you haven’t you get absolutely nothing!

We can address that with a short snippet of code and the trusty content:file:afterParse hook in much the same way as we did for the reading time plugin. Create a new file (e.g. content-excerpts.ts in your server\plugins with the following contents:

import type { MarkdownNode, MarkdownRoot, ParsedContent } from "@nuxt/content"

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook("content:file:afterParse", (file) => {
    if (file._id.endsWith(".md") && file.body) {
        addExcerpt(file)
      }
    }
  })
})

const addExcerpt = (file: ParsedContent) => {
  if (file.excerpt) return

  const excerpt: MarkdownRoot = { type: "root", children: [] }
  let paragraphsToInclude = 2

  visit(file.body!.children, (n) => {
    if (paragraphsToInclude == 0) return
    if (n?.tag == "p") {
      paragraphsToInclude--
      excerpt.children.push(withRemovedLinks(n))
    }
  })

  file.excerpt = excerpt
}

const withRemovedLinks = (n: MarkdownNode): MarkdownNode => {
  const node: MarkdownNode = {
    type: n.type,
    value: n.value,
    tag: n.tag,
    props: n.props,
    attributes: n.attributes,
    children: [] as MarkdownNode[],
  }

  visit(n.children!, (c) => {
    if (c.tag != "a") {
      node.children?.push(c)
    }
  })

  return node
}

0 responses to Generated Excerpts for Nuxt3 Content

  1. Avatar for

    Information is only used to show your comment. See my Privacy Policy.