npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gatsby-plugin-excerpts

v1.1.0

Published

A powerful way to extract excerpts from content

Readme

gatsby-plugin-excerpts

This plugin provides a powerful way to extract excerpts from content. The excerpts might be useful for providing content previews on listing pages or for generating SEO descriptions.

Features

  • Works with any source/transformer (including gatsby-transformer-remark)
  • Locate excerpts using CSS selectors
  • Limit excerpts to a certain character/word count while preserving formatting (thanks to truncate-html)
  • Apply common transformations to your excerpts, such as removing images or stripping links
  • Retrieve multiple different excerpts for each node
  • Specify fallback behaviour if your preferred excerpt can't be found
  • Fully-typed via TypeScript

Documentation

Configuration options are documented via TSDoc in the ConfigurationTypes file.

If you're using TypeScript, you can import these types to get documentation within your IDE. (E.g. via IntelliSense)

// At the top of gatsby-config.ts
import { demand } from 'ts-demand'; // Optional, but makes it easier to apply typechecking to your config
import { ConfigurationTypes as ExcerptConfigurationTypes } from 'gatsby-plugin-excerpts';

// Then, in your plugin list
module.exports = {
    // ...
    plugins: [
        // ...
        {
            resolve: `gatsby-plugin-excerpts`,
            options: demand<ExcerptConfigurationTypes.IExcerptsPluginConfiguration>({
                // ...
            })
        }
    ]
};

Example

This example adds an excerpt called 'snippet' to pages generated by gatsby-transformer-remark and allows content authors to mark snippets using gatsby-remark-custom-blocks. If the author has not marked any snippets, the first 80 words will be used instead, excluding any images or gatsby-remark-prismjs code blocks.

The snippet will have all links stripped, and the headings will be adjusted down one level. (So h2 becomes h3, h3 becomes h4, etc...) This might be useful for a blog, as on a post page the post title will probably be an h1, while on a listing you might have the listing title as an h1 and each post title as an h2.

gatsby-remark-custom-blocks config (must be within the gatsby-transformer-remark plugins object)

{
    "resolve": "gatsby-remark-custom-blocks",
    "options": {
        "blocks": {
            "snippet": {
                "classes": "snippet"
            },
        },
    },
}

gatsby-plugin-excerpts config

{
    "resolve": "gatsby-plugin-excerpts",
    "options": {
        "sources": {
            "snippetBlocks": {
                "type": "htmlQuery",
                "sourceField": "html",
                "excerptSelector": ".custom-block.snippet .custom-block-body",
                "stripSelector": "a",
                "elementReplacements": [
                    {
                        "selector": "h6",
                        "replaceWith": "strong"
                    },
                    {
                        "selector": "h5",
                        "replaceWith": "h6"
                    },
                    {
                        "selector": "h4",
                        "replaceWith": "h5"
                    },
                    {
                        "selector": "h3",
                        "replaceWith": "h4"
                    },
                    {
                        "selector": "h2",
                        "replaceWith": "h3"
                    },
                ],
            },
            "default": {
                "type": "htmlQuery",
                "sourceField": "html",
                "excerptSelector": "html > *",
                "ignoreSelector": "img, .gatsby-highlight",
                "stripSelector": "a",
                "elementReplacements": [
                    {
                        "selector": "h6",
                        "replaceWith": "strong"
                    },
                    {
                        "selector": "h5",
                        "replaceWith": "h6"
                    },
                    {
                        "selector": "h4",
                        "replaceWith": "h5"
                    },
                    {
                        "selector": "h3",
                        "replaceWith": "h4"
                    },
                    {
                        "selector": "h2",
                        "replaceWith": "h3"
                    },
                ],
                "truncate": {
                    "length": 80,
                    "byWords": true,
                    "ellipsis": "…"
                },
            }
        },
        "sourceSets": {
            "markdownHtml": [
                "snippetBlocks",
                "default"
            ]
        },
        "excerpts": {
            "snippet": {
                "type": "html",
                "nodeTypeSourceSet": {
                    "MarkdownRemark": "markdownHtml"
                }
            }
        }
    },
}

Display in components

Then you can just add 'snippet' to your GraphQL queries to retrieve the excerpt called 'snippet'.

query {
    site {
        siteMetadata {
            title
        }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
        edges {
            node {
                snippet
                html
                fields {
                    slug
                }
                frontmatter {
                    date(formatString: "YYYY-MM-DD")
                    title
                }
            }
        }
    }
}

If your excerpt's output type is "html" (which it is in this example) then you will want to render it in your template/component using dangerouslySetInnerHTML.

<div className="snippet" dangerouslySetInnerHTML={{ __html: article.snippet }} />

Usage in content

Now when you write content, you can specify blocks to include within the snippet.

When I was a child, I used to spend long hours thinking about how every blog post needed a paragraph of irrelevant drivel at the start of it. Such content satisfies the content writer's desire to tell a story with their post, and they narcissistically convince themselves that others would want to read this story. But the problem is that when that blog post appears on a listing page or in search engine results, the excerpt often just displays the first few characters of the post. If the post starts with inanity the viewer may not click on the link.

[[snippet]]
| Wouldn't it be nice if you could retrieve excerpts from anywhere within the post? If the author could *choose* which paragraphs would be used as the snippet?
| 
| Well now you can.

Thanks to the useful Gatsby plugin gatsby-plugin-excerpts, you can create custom rules that blah blah blah blah...

The paragraphs within the snippet block (the 2nd and 3rd paragraphs) will be used as the snippet.

FAQ

Couldn't I achieve the same thing within my template's JavaScript?

You can achieve the same end result within your template/component, but with additional performance/bandwidth costs. When a user first visits your site, the initial content is delivered to them via a static HTML file. However as they navigate around your site, subsequent page loads are achieved by GraphQL query results being delivered via AJAX and the template being rendered clientside.

Because searching for excerpts within content requires the entire content to be loaded, this means that if a user visits a listing page that contains excerpts for 25 other pages, the user's browser must download the entire contents of those 25 other pages just to display the listing. This is wasteful.

Additionally, there is an (admittedly small) amount of computation required to locate the excerpts. Do you really want to force your user's device to do extra work every time they load a page? Gatsby is about generating static sites, and this plugin allows you to statically generate your excerpts.

Why isn't this a gatsby-remark-* plugin?

It would've been much easier to write this plugin to be a plugin for gatsby-transformer-remark and it would also be easier to use. But I think too much of the Gatsby ecosystem is exclusive to Remark users, and I wanted to show alternative transformers some love. You can use this plugin with any source & transformer.

Can I use this plugin to retrieve excerpts from Front Matter or other metadata?

Sorry, not currently. This plugin only allows retrieving excerpts from page content. Front Matter can already be delivered efficiently via GraphQL, so I don't see it as a priority. If you have a use-case for retrieving excerpts from metadata within this plugin, please let me know and I might reconsider.

How many times did you misspell 'excerpt' while writing this plugin?

Too many. Hopefully none of the mistakes made it into Git. Also I definitely have semantic satiation for that word now.

Future plans

  • Add functionality for searching for excerpts within plaintext content
  • Add functionality for converting content before it hits a searcher (e.g. convert Markdown to HTML, or HTML to plaintext)
  • Add system for using GraphQL arguments to modify excerpt behaviour
  • Add support for chaining together multiple sources/processors (maybe turn this into a general purpose content transmogrification engine?)
  • Find a better way to resolve values of other GraphQL fields (if possible)