Enhance Backstage MarkdownContent With Extensibility Props

Alex Johnson
-
Enhance Backstage MarkdownContent With Extensibility Props

Hey guys! Today, we're diving deep into a discussion about improving Backstage's MarkdownContent component. This is super important because it affects how we render Markdown, which is like, everywhere in Backstage. We're talking about making it more flexible and powerful, so let’s get started!

The Current Situation: Why We Need Extensibility

Currently, the MarkdownContent component in Backstage, while useful, has some limitations. It wraps ReactMarkdown, which is fantastic, but it doesn't expose all of ReactMarkdown's cool features, specifically its extensibility options. Think of it like having a super cool car but not being able to add any aftermarket parts – a bummer, right?

Diving Deep into the Problem

The main issue is that we can't easily add plugins to extend the markdown rendering capabilities. These plugins are crucial for things like rendering diagrams, handling special syntax, and more. The existing MarkdownContent component can be found here. To understand the missing piece, check out ReactMarkdown's extensibility options.

To put it simply, the MarkdownContent component should allow the use of remarkPlugins, rehypePlugins, and components to extend its functionality. Currently, it doesn't, and this is where our problem lies. Without these options, we're stuck with basic Markdown rendering, which isn't always enough.

For example, the ADR plugin needs to support Mermaid diagrams (as highlighted in this issue). The current workaround involves some content splitting magic, which isn't ideal because it doesn't leverage the existing remark/rehype ecosystem. It's like trying to build a house with only a hammer when you have a whole toolbox available!

The Impact of Limited Extensibility

The lack of extensibility in the MarkdownContent component leads to several issues:

  1. Inability to Render Complex Content: Without plugin support, rendering complex elements like diagrams or custom syntax becomes a challenge.
  2. Workarounds and Hacks: Developers resort to workarounds, such as custom parsing logic, which can be time-consuming and error-prone.
  3. Inconsistent Markdown Rendering: Different plugins might implement their own Markdown rendering logic, leading to inconsistencies across the platform.
  4. Missed Opportunities: We miss out on the vast ecosystem of remark and rehype plugins that could enhance our Markdown rendering capabilities.

So, it's clear that enhancing the MarkdownContent component with extensibility options is not just a nice-to-have; it's a necessity for a more flexible and powerful Backstage experience. Let's move on to the proposed solution!

The Proposal: Adding Extensibility Props

Okay, so how do we fix this? The proposal is straightforward: we add optional extensibility props to the MarkdownContent component. This means allowing developers to pass in remarkPlugins, rehypePlugins, and components to customize the Markdown rendering process.

The Technical Details

Here’s the proposed code snippet:

type Props = {
  // ... existing props
  remarkPlugins?: PluggableList;
  rehypePlugins?: PluggableList;
  components?: Partial<Components>;
};

export function MarkdownContent(props: Props) {
  const {
    remarkPlugins = [],
    rehypePlugins = [],
    components: userComponents = {},
    ...rest
  } = props;
  
  const finalRemarkPlugins =
    dialect === 'gfm' ? [gfm, ...remarkPlugins] : remarkPlugins;
  const finalComponents = { ...defaultComponents, ...userComponents };
  
  return (
    <ReactMarkdown
      remarkPlugins={finalRemarkPlugins}
      rehypePlugins={rehypePlugins}
      components={finalComponents}
      {...rest}
    />
  );
}

Let's break this down:

  • New Props: We're adding three new optional props: remarkPlugins, rehypePlugins, and components. These props allow developers to provide their own plugins and component overrides.
  • PluggableList: This type represents a list of remark or rehype plugins. It could include things like syntax highlighting, diagram rendering, or custom Markdown transformations.
  • Partial: This type allows developers to override specific React components used by ReactMarkdown. For example, you might want to customize how code blocks or headings are rendered.
  • Default Components: The code merges user-provided components with the default components, ensuring that the base styling and functionality of MarkdownContent are preserved.
  • finalRemarkPlugins: This ensures that the gfm (GitHub Flavored Markdown) plugin is included by default when the dialect is set to gfm, while still allowing custom plugins to be added.

Usage Example: Adding Mermaid Diagram Support

To illustrate how this would work, let's look at an example of adding Mermaid diagram support:

<MarkdownContent
  content={content}
  components={{
    code: ({ className, children }) => {
      const language = /language-(\w+)/.exec(className)?.[1];
      if (language === 'mermaid') return <MermaidDiagram code={children} />;
      return <code className={className}>{children}</code>;
    }
  }}
/>

In this example, we're overriding the code component to detect Mermaid code blocks and render them using a <MermaidDiagram> component. This is a clean and efficient way to extend the functionality of MarkdownContent without resorting to hacks or workarounds.

Benefits of the Proposal

Adding these extensibility props has several key benefits:

  • Flexibility: Developers can customize Markdown rendering to fit their specific needs.
  • Reusability: Plugins can be shared and reused across different parts of the Backstage ecosystem.
  • Maintainability: We avoid custom parsing logic and workarounds, leading to cleaner and more maintainable code.
  • Consistency: By leveraging the remark/rehype ecosystem, we ensure consistent Markdown rendering across plugins.

Exploring Alternatives: Why This Approach Wins

Okay, so why are we proposing this approach instead of other options? Let's take a look at some alternatives and why they don't quite measure up.

Alternative 1: Using ReactMarkdown Directly in Plugins

One alternative is to simply use ReactMarkdown directly in plugins. While this might seem like a quick fix, it has some serious drawbacks.

  • Loss of Backstage's Theming and Styling: ReactMarkdown doesn't automatically inherit Backstage's theming and styling. This means you'd have to duplicate styling code in every plugin that uses ReactMarkdown, which is a huge pain.
  • Inconsistent Markdown Rendering: Without a centralized component like MarkdownContent, different plugins might render Markdown in different ways. This can lead to a fragmented and inconsistent user experience.
  • Maintenance Overhead: Each plugin would be responsible for maintaining its own ReactMarkdown configuration, leading to code duplication and increased maintenance overhead.

Alternative 2: Content Splitting Workarounds

As mentioned earlier, the current approach in the ADR plugin involves content splitting workarounds. This means parsing Markdown separately to handle specific elements, like Mermaid diagrams. While this works, it's not ideal.

  • Doesn't Leverage the Plugin Ecosystem: Content splitting doesn't take advantage of the remark/rehype plugin ecosystem. You're essentially reinventing the wheel every time you need to support a new Markdown extension.
  • Requires Custom Parsing Logic: You need to write custom parsing logic for each extension, which can be complex and error-prone.
  • Not Scalable: This approach doesn't scale well as you add more Markdown extensions. The parsing logic can become increasingly complex and difficult to maintain.

Alternative 3: Copy and Maintain MarkdownContent per Plugin

Another option is to copy the MarkdownContent component into each plugin that needs it. This is a recipe for disaster!

  • Code Duplication: This leads to a massive amount of code duplication across the ecosystem.
  • Maintenance Burden: Every time you need to update the MarkdownContent component, you'd have to update it in every plugin that has a copy. Talk about a maintenance nightmare!
  • Inconsistent Behavior: Even with careful maintenance, it's easy for different copies of the component to diverge over time, leading to inconsistent behavior across the platform.

Why Our Proposal Wins

Our proposal of adding extensibility props to MarkdownContent strikes the right balance between flexibility, maintainability, and consistency. It allows developers to customize Markdown rendering without sacrificing the benefits of a centralized component. It's the clear winner in terms of long-term sustainability and developer experience.

Conclusion: Let's Make Markdown Content Awesome!

So, guys, we've covered a lot here. We've identified the limitations of the current MarkdownContent component, proposed a solution, and explored alternatives. It's clear that adding extensibility props is the way to go. This will make Backstage more flexible, powerful, and consistent.

By allowing developers to use remark and rehype plugins, we're unlocking a whole new world of possibilities for Markdown rendering in Backstage. We can support complex content, ensure consistent styling, and reduce the need for workarounds and hacks.

I'm excited to see this proposal move forward and make Backstage's MarkdownContent component even more awesome!

For more information on React Markdown and its extensibility, check out the official React Markdown documentation. It's a great resource for understanding how plugins work and how to customize your Markdown rendering.

You may also like