Skip to content

RSS Improvements #8544

Description

@bmuenzenmeyer

Enter your suggestions in details:

I got feedback that RSS posts would be useful if they had more preview context. For example, this is what blasts into Slack...

Image

I haven't looked at other feed experiences. It could be variable.

Activity

  1. 10hendersonm commented on Jan 13, 2026

    @10hendersonm

    I looked into this a bit, I think the Node.js® is a free, open-source, cross-platform... is not coming from the RSS config, but rather the RSS is pointing to a page which Slack (or other tools which perform link previews) is reading a meta tag from. The meta tag looks to be driven from the individual article's description frontmatter property, or if none exists then the site's description property.

    My notes on how all this flows together

    https://nodejs.org/en/feed/blog.xml contains this object:

    <title>
    <![CDATA[ Node.js 22.22.0 (LTS) ]]>
    </title>
    <link>https://nodejs.org/en/blog/release/v22.22.0</link>
    <guid isPermaLink="false">/blog/release/v22.22.0?1768313889314</guid>
    <pubDate>Tue, 13 Jan 2026 14:18:09 GMT</pubDate>
    </item>

    Grabbing https://nodejs.org/en/blog/release/v22.22.0 contains the page description in the <meta name="description" content="..." /> tag:

    Image

    For the blog specifically, that metadata is being generated into Next.js using a generateMetadata export:

    // This generates each page's HTML Metadata
    // @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata
    export const generateMetadata = ({ params }: PageParams) =>
    basePage.generateMetadata({ params, prefix: 'blog' });

    /**
    * This generates each page's HTML Metadata
    *
    * @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata
    *
    * @param {{ params: Promise<{ path: Array<string>; locale: string }>, prefix?: string }} props
    * @returns {Promise<import('next').Metadata>} the metadata for the page
    */
    export const generateMetadata = async ({ params, prefix }) => {
    const { path = [], locale = defaultLocale.code } = await params;
    const pathname = dynamicRouter.getPathname(path);
    return dynamicRouter.getPageMetadata(
    locale,
    // If there's a prefix, `join` it with the pathname
    prefix ? join(prefix, pathname) : pathname
    );
    };

    /**
    * This method generates the Next.js App Router Metadata
    * that can be used for each page to provide metadata
    *
    * @param {string} locale
    * @param {string} path
    * @returns {Promise<import('next').Metadata>}
    */
    const _getPageMetadata = async (locale = defaultLocale.code, path = '') => {
    const pageMetadata = { ...PAGE_METADATA };
    const { source = '' } = await getMarkdownFile(locale, path);
    const { data } = matter(source);
    const getUrlForPathname = (l, p) =>
    `${baseUrlAndPath}/${l}${p ? `/${p}` : ''}`;
    // Default Title for the page
    pageMetadata.title = data.title
    ? `${siteConfig.title} — ${data.title}`
    : siteConfig.title;
    pageMetadata.description = data.description
    ? data.description
    : siteConfig.description;
    // Default Twitter Title for the page
    pageMetadata.twitter.title = pageMetadata.title;
    // Default Open Graph Image for the page
    pageMetadata.openGraph.images = [
    ENABLE_STATIC_EXPORT
    ? `${defaultLocale.code}/next-data/og/announcement/Run JavaScript Everywhere`
    : `${defaultLocale.code}/next-data/og/${data.category ?? DEFAULT_CATEGORY_OG_TYPE}/${pageMetadata.title}`,
    ];
    // Default canonical URL for the page
    pageMetadata.alternates.canonical =
    data.canonical ?? getUrlForPathname(locale, path);
    // Default alternate URL for the page in the default locale
    pageMetadata.alternates.languages['x-default'] = getUrlForPathname(
    defaultLocale.code,
    path
    );
    // Retrieves a matching blog feed for the category of the blog post
    // If no matching blog feed is found, we simply fallback to the default blog feed
    const matchingBlogFeed = siteConfig.rssFeeds.find(
    feed => feed.category === data.category
    );
    // Adds the RSS Feed URL to the page metadata, if a matching feed is found
    // otherwise, we fallback to the default blog feed
    pageMetadata.alternates.types['application/rss+xml'] = getUrlForPathname(
    locale,
    `feed/${matchingBlogFeed?.file ?? 'blog.xml'}`
    );
    // Iterate all languages to generate alternate URLs for each language
    availableLocaleCodes.forEach(currentLocale => {
    pageMetadata.alternates.languages[currentLocale] = getUrlForPathname(
    currentLocale,
    path
    );
    });
    return pageMetadata;
    };
    // Creates a Cached Version of the Page Metadata Context
    const getPageMetadata = cache(async (locale, path) => {
    return await _getPageMetadata(locale, path);
    });

    Data from_getPageMetadata is either sourced from the specific markdown article's frontmatter content or from the entire site's config:

    "description": "Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.",

  2. ovflowd commented on Jan 23, 2026

    @ovflowd
    Member

    Well, the problem is that we don't really have summaries for our blog posts. Just the content, and authors aren't used on writing summaries.

  3. ovflowd commented on Jan 23, 2026

    @ovflowd
    Member

    We could source the first paragraph form the article itself ... but not sure if that'd be a good idea.

  4. bmuenzenmeyer commented on Feb 1, 2026

    @bmuenzenmeyer
    ContributorAuthor

    so we'd have to institute a process change for releasers to write some sort of top-level description, right? certainly puts a bit of a wrench in the automation

  5. araujogui commented on Feb 1, 2026

    @araujogui
    Member

    Maybe we could generate summaries with AI

  6. ovflowd commented on Feb 8, 2026

    @ovflowd
    Member

    Maybe we could generate summaries with AI

    That'd be a bit expensive... Where would we store them or when to generate them?

  7. ovflowd commented on Feb 8, 2026

    @ovflowd
    Member

    so we'd have to institute a process change for releasers to write some sort of top-level description, right? certainly puts a bit of a wrench in the automation

    🫠

  8. araujogui commented on Feb 13, 2026

    @araujogui
    Member

    Maybe we could generate summaries with AI

    That'd be a bit expensive... Where would we store them or when to generate them?

    Why expensive? The posts aren't that long. We can automate this using actions and can save the result on the post's frontmatter

  9. ovflowd commented on Feb 14, 2026

    @ovflowd
    Member

    Maybe we could generate summaries with AI

    That'd be a bit expensive... Where would we store them or when to generate them?

    Why expensive? The posts aren't that long. We can automate this using actions and can save the result on the post's frontmatter

    Who paying the AI bill, you?

  10. ovflowd commented on Feb 14, 2026

    @ovflowd
    Member

    Cause automated (non-human-originating) AI agentic/task usage is against the ToS of pretty much every provider out there.

  11. MattIPv4 commented on Mar 6, 2026

    @MattIPv4
    Member

    👀 I think we'd be fine to set the RSS description to the first paragraph of the article, with links + markup stripped out.

  12. AugustinMauroy commented on Mar 7, 2026

    @AugustinMauroy
    Member

    let's try Matt idea it's seem good idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    contentIssues/pr concerning contentdesign

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions