Adding Adaptive Cards to a React App with Markdown Support

When I started working with Adaptive Cards in a React app, I wanted a way to dynamically load and style cards while keeping them flexible. Adaptive Cards provide a powerful way to render dynamic UI elements using JSON, but by default, their Markdown support is somewhat limited. To improve this, I decided to integrate markdown-it, a robust Markdown parser, for better formatting.

Thank me by sharing on Twitter 🙏

I’ll walk through how I got Adaptive Cards working with templating and Markdown support in React.

1. Installing the Required Libraries

Since I was already using React, I needed to install a few dependencies. Adaptive Cards require the adaptivecards package, and for dynamic data binding, adaptivecards-templating is useful. To improve Markdown rendering, I added markdown-it.

To get everything installed, I ran:

Plaintext
npm install adaptivecards adaptivecards-templating markdown-it

With that, the required dependencies were in place.

2. Creating an Adaptive Card Component

The next step was to create a reusable React component to render Adaptive Cards dynamically. This component needed to accept a card template and data, apply templating, and then render the final card.

Here’s the component I built:

TypeScript
import * as AC from "adaptivecards";
import { useEffect, useRef } from "react";
import { Template } from "adaptivecards-templating";
import MarkdownIt from "markdown-it";

interface AdaptiveCardProps {
	cardPayload: string;
	data?: Record<string, any>;
}

export const AdaptiveCardContainer = ({ cardPayload, data }: AdaptiveCardProps) => {
	const cardContainer = useRef<HTMLDivElement>(null);

	useEffect(() => {
		if (cardPayload) {
			try {
				const template = new Template(cardPayload);

				const expandedTemplate = template.expand({
					$root: data,
				});

				const adaptiveCard = new AC.AdaptiveCard();

				adaptiveCard.hostConfig = new AC.HostConfig({
					supportsInteractivity: true,
				});
				
				// Configure Markdown rendering using markdown-it
				const md = new MarkdownIt();
				AC.AdaptiveCard.onProcessMarkdown = (text, result) => {
					result.outputHtml = md.render(text);
					result.didProcess = true;
				};

				adaptiveCard.parse(expandedTemplate);

				// Render the card and append it to the container
				const renderedCard = adaptiveCard.render();
				if (cardContainer.current) {
					cardContainer.current.innerHTML = "";
					cardContainer.current.appendChild(renderedCard);
				}

				// Handle actions (submit, openUrl, etc.)
				adaptiveCard.onExecuteAction = (action) => {
					if (action instanceof AC.SubmitAction) {
						console.log("Submitted data:", action.data);
					}
				};
			} catch (error) {
				console.log("Error rendering Adaptive Card:", error);
			}
		}
	}, [cardPayload, data]);

	return <div ref={cardContainer} />;
};

This component handles templating, rendering, and Markdown formatting all in one place.

3. Using the Component in the App

With the component ready, I needed to define a JSON template and pass some dynamic data into it. Adaptive Cards allow for placeholders that can be replaced dynamically using the templating engine.

Here’s how I used it inside my React app:

TypeScript
import AdaptiveCardComponent from "./AdaptiveCardComponent";

const cardTemplate = {
  type: "AdaptiveCard",
  version: "1.4",
  body: [
    {
      type: "TextBlock",
      text: "**Hello, ${name}!**",
      weight: "Bolder",
      size: "Medium",
    },
    {
      type: "TextBlock",
      text: "_Your favorite color is **{favoriteColor}**._",
    },
    {
      type: "TextBlock",
      text: "- List item one\n- List item two",
    },
    {
      type: "Input.Text",
      id: "userInput",
      placeholder: "Enter something...",
    },
  ],
  actions: [
    {
      type: "Action.Submit",
      title: "Submit",
    },
  ],
};

const data = {
  name: "John Doe",
  favoriteColor: "Blue",
};

const App = () => {
  return (
    <div>
      <h1>Adaptive Cards with Markdown Support</h1>
      <AdaptiveCardComponent cardPayload={cardTemplate} data={data} />
    </div>
  );
};

export default App;

This setup allowed the Adaptive Card to display dynamic content based on the data object. Markdown formatting also worked correctly inside text blocks.

4. Handling Markdown in Adaptive Cards

By default, Adaptive Cards provide basic Markdown support, but I found it limiting. Using markdown-it, I was able to enable better formatting, including lists, bold, italic, and more.

The key change was this line inside the component:

TypeScript
const md = new MarkdownIt();
AC.AdaptiveCard.onProcessMarkdown = (text, result) => {
  result.outputHtml = md.render(text);
  result.didProcess = true;
};

This ensures that all Markdown in the Adaptive Card text blocks is processed through markdown-it before being rendered.

5. Testing and Expanding Features

With this setup, I was able to dynamically load Adaptive Cards, apply templating, and ensure that Markdown worked as expected. I also tested different Markdown features like bullet points, headings, and inline links, all of which worked well.

If I wanted to enhance this further, I could integrate an API to fetch Adaptive Card templates dynamically or even add support for more Markdown extensions. But for now, this approach worked perfectly for rendering rich, flexible UI components in React.

Share this:

Leave a Reply