MML for React
MML React is a library from GetStream for rendering Message Markup Language (MML) content within React applications. It provides a foundational `<MML />` component that parses MML strings and translates them into corresponding React components for display. The library is currently stable at version `0.4.7` and has seen a series of maintenance and dependency updates, particularly around early 2022, though new feature releases appear less frequent. Key differentiators include its tight integration with the GetStream ecosystem for building rich chat UIs, extensive customization options for overriding default MML tag-to-React component mappings, and a flexible theming system using SCSS variables. It abstracts away the complexity of parsing and rendering structured message content, offering a component-based approach to build interactive and styled UI elements.
Common errors
-
Module not found: Can't resolve 'mml-react'
cause The 'mml-react' package is not installed in your project, or the import path is incorrect.fixRun `npm install mml-react` or `yarn add mml-react`. Ensure your import statement is `import { MML } from 'mml-react';`. -
TypeError: MML is not a function
-
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components)...Check the render method of `YourComponent`.
cause You are attempting to use `MML` as a function or a default import, or it's not being rendered as a valid React component.fixEnsure `MML` is imported as a named export (`import { MML } from 'mml-react';`) and used as a JSX component: `<MML source="<mml>...</mml>" />`. -
Error: MML parsing failed: [specific XML parsing error message, e.g., 'Unexpected token < at line 1 column 6']
cause The string provided to the `source` prop contains malformed or invalid MML, which the XML parser cannot process.fixReview the `mmlContent` or `source` prop string for well-formed XML structure. Ensure all tags are properly closed, attributes are quoted, and the overall MML conforms to the expected specification. -
Warning: Failed prop type: Invalid prop `converters` supplied to `MML`.
cause The `converters` prop is not an object, or its values (the converter functions) are not correctly structured or return valid React elements.fixEnsure the `converters` prop is an object where keys are MML tag names (e.g., `'button'`) and values are functions that accept `tag` and `children` arguments and return a React element.
Warnings
- breaking The package upgraded `react-virtuoso` to v1. If your application indirectly relied on specific `react-virtuoso` behaviors or props through `mml-react`, you might encounter breaking changes related to list virtualization.
- breaking The underlying `xml-parser` dependency was upgraded to v3. This could introduce subtle parsing differences or stricter validation for MML source strings, potentially affecting rendering of malformed or edge-case MML inputs.
- gotcha React v18 support was added and peer dependencies were updated. Older versions of `mml-react` might not be fully compatible with React v18, leading to warnings or runtime errors.
- breaking The `@braintree/sanitize-url` dependency was bumped to a major version (v6). As a security-related library, this may introduce breaking changes in URL sanitization logic, potentially altering how URLs are processed or rendered within MML content, or stricter filtering of previously allowed URLs.
- breaking A critical security fix was implemented to forbid access to object prototype properties within the MML parser, addressing a potential prototype pollution or similar vulnerability. Applications using versions prior to this fix are vulnerable.
Install
-
npm install mml-react -
yarn add mml-react -
pnpm add mml-react
Imports
- MML
const MML = require('mml-react');import { MML } from 'mml-react'; - MMLProps
import type { MMLProps } from 'mml-react';
Quickstart
import React, { useState } from 'react';
import { MML } from 'mml-react';
// Example of custom converter for the 'button' tag
const customButtonConverter = (tag: any, children: React.ReactNode) => {
const { text, action } = tag.node.attributes;
return (
<button
key={tag.key}
onClick={() => {
// In a real application, parse 'action' safely or use predefined callbacks
if (action && action.startsWith('console.log')) {
// eslint-disable-next-line no-eval
eval(action); // DANGER: Evaluate untrusted input with caution
} else if (action && action.startsWith('alert')) {
// eslint-disable-next-line no-eval
eval(action);
}
}}
style={{
backgroundColor: '#6200EE',
color: 'white',
padding: '8px 16px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
margin: '5px 0',
fontWeight: 'bold',
}}
>
{text || children || 'Default Button'}
</button>
);
};
export default function MMLDemo() {
const [mmlContent, setMmlContent] = useState<string>(`
<mml>
<card>
<card-header>Welcome to MML React!</card-header>
<card-body>
<text>This is an example of <strong color="primary-accent">Message Markup Language</strong> rendered in React.</text>
<image url="https://picsum.photos/seed/mml/300/150" alt="Random image" />
<button text="Log to console" action="console.log('Button clicked from MML!')" />
<button text="Show Alert" action="alert('Hello from MML!')" />
<input type="text" placeholder="Enter something..." />
<row>
<col width="50%"><text>Column 1</text></col>
<col width="50%"><text>Column 2</text></col>
</row>
</card-body>
</card>
</mml>
`);
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMmlContent(e.target.value);
};
return (
<div style={{ fontFamily: 'sans-serif', maxWidth: '600px', margin: '20px auto', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h1>MML React Live Demo</h1>
<p>Edit the MML source below to see changes reflected instantly.</p>
<textarea
value={mmlContent}
onChange={handleInputChange}
rows={15}
style={{ width: '100%', minHeight: '200px', marginBottom: '20px', padding: '10px', borderColor: '#ccc', borderRadius: '4px' }}
/>
<h2>Rendered MML:</h2>
<div style={{ border: '1px solid #eee', padding: '15px', borderRadius: '5px', backgroundColor: '#f9f9f9' }}>
<MML source={mmlContent} converters={{ button: customButtonConverter }} />
</div>
</div>
);
}