Custom Component Libraries
The UIResourceRenderer
component allows hosts to provide their own component libraries and custom element definitions for remoteDom
resources. It enables rendering server-defined UI using the host's existing component system, ensuring a consistent look and feel.
You can provide your own library and elements through the remoteDomProps
prop.
remoteDomProps
The remoteDomProps
prop is an object that accepts two optional properties:
library
: AComponentLibrary
object that maps remote element tag names to your React components.remoteElements
: An array ofRemoteElementConfiguration
objects that define the properties and events for your custom elements.
ComponentLibrary
A ComponentLibrary
is an object with two properties:
name
: A string that identifies your library.elements
: An array of objects that map element tag names to your components.
Each element object has the following properties:
tagName
: The name of the remote element (e.g.,my-button
).component
: The React component to render for this element.propMapping
: An object that maps remote element attributes to your component's props.eventMapping
: An object that maps remote element events to your component's event handlers.
RemoteElementConfiguration
A RemoteElementConfiguration
object defines the properties and events for a custom element. It has the following properties:
tagName
: The name of the remote element.properties
: An object that defines the properties for the element.events
: An object that defines the events for the element.
Example: Custom React Components
Let's walk through an example of how to create and use a custom component library with UIResourceRenderer
. We'll create a simple library with a custom button and text component.
1. Define Your Components
First, create your React components. These can be any valid React component.
// MyButton.tsx
import React from 'react';
interface MyButtonProps {
label: string;
onCustomPress: () => void;
}
export const MyButton: React.FC<MyButtonProps> = ({ label, onCustomPress }) => {
return <button onClick={onCustomPress}>{label}</button>;
};
// MyText.tsx
import React from 'react';
interface MyTextProps {
text: string;
}
export const MyText: React.FC<MyTextProps> = ({ text }) => {
return <p>{text}</p>;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2. Create the Component Library
Next, create a ComponentLibrary
object that maps your remote element tag names to your components.
// my-library.ts
import { MyButton } from './MyButton';
import { MyText } from './MyText';
export const myLibrary = {
name: 'my-library',
elements: [
{
tagName: 'my-button',
component: MyButton,
propMapping: {
label: 'label',
},
eventMapping: {
press: 'onCustomPress',
},
},
{
tagName: 'my-text',
component: MyText,
propMapping: {
content: 'text',
},
},
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3. Define the Remote Elements
Now, define the RemoteElementConfiguration
for your custom elements.
// my-elements.ts
export const myElements = [
{
tagName: 'my-button',
properties: {
label: { type: String },
},
events: {
press: {},
},
},
{
tagName: 'my-text',
properties: {
content: { type: String },
},
},
];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4. Use in UIResourceRenderer
Finally, pass your custom library and elements to UIResourceRenderer
via the remoteDomProps
prop.
// App.tsx
import React from 'react';
import { UIResourceRenderer } from '@mcp-ui/client';
import { myLibrary } from './my-library';
import { myElements } from './my-elements';
function App({ resource }) {
return (
<UIResourceRenderer
resource={resource}
remoteDomProps={{
library: myLibrary,
remoteElements: myElements,
}}
/>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
5. Server-Side Script
The server can now send a script that uses your custom elements.
const button = document.createElement('my-button');
button.setAttribute('label', 'Click Me');
button.addEventListener('press', () => {
console.log('Button pressed!');
});
const text = document.createElement('my-text');
text.setAttribute('content', 'Hello from a custom library!');
root.appendChild(text);
root.appendChild(button);
2
3
4
5
6
7
8
9
10
11
When UIResourceRenderer
renders this resource, it will use your MyButton
and MyText
components, mapping the attributes and events as you defined.
Integration with Web Components
@mcp-ui/client
also supports rendering remoteDom
resources as Web Components. This allows you to use standard HTML elements in your host application, which can be useful for interoperability or for use in frameworks other than React.
To use Web Components, you need to specify framework=webcomponents
in the mimeType
of the resource.
1. Define Your Web Components
First, define your custom elements as Web Components.
// my-web-components.ts
class MyButton extends HTMLElement {
// ... implementation ...
}
class MyText extends HTMLElement {
// ... implementation ...
}
export function defineWebComponents() {
if (!customElements.get('my-button')) {
customElements.define('my-button', MyButton);
}
if (!customElements.get('my-text')) {
customElements.define('my-text', MyText);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. Register Web Components in Your App
In your application's entry point, call the function to register the Web Components.
// main.tsx
import { defineWebComponents } from './my-web-components';
defineWebComponents();
2
3
4
3. Server-Side Script
The server-side script remains the same, but the mimeType
of the resource must be updated.
const resource = {
mimeType: 'application/vnd.mcp-ui.remote-dom+javascript; framework=webcomponents',
text: `
const button = document.createElement('my-button');
button.setAttribute('label', 'Click Me');
button.addEventListener('press', () => {
console.log('Button pressed!');
});
const text = document.createElement('my-text');
text.setAttribute('content', 'Hello from a Web Component!');
root.appendChild(text);
root.appendChild(button);
`,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. Use in UIResourceRenderer
When using Web Components, you can often use the basicComponentLibrary
provided by @mcp-ui/client
as it doesn't need to do any special rendering. UIResourceRenderer
will detect the webcomponents
framework in the mime type and will render the elements as standard HTML elements.
// App.tsx
import React from 'react';
import { UIResourceRenderer, basicComponentLibrary } from '@mcp-ui/client';
import { myElements } from './my-elements'; // Same element definitions as before
function App({ resource }) {
return (
<UIResourceRenderer
resource={resource}
remoteDomProps={{
library: basicComponentLibrary,
remoteElements: myElements,
}}
/>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Examples in MCP-UI
You can find a complete, working example of custom component libraries in the remote-dom-demo
directory in the examples
folder of this repository.
- Demo Application:
examples/remote-dom-demo/src/App.tsx
- Radix UI Library:
examples/remote-dom-demo/src/libraries/radix.tsx
- Web Components Library:
examples/remote-dom-demo/src/libraries/webcomponents.ts
Further Reading
For more information on the underlying technology, see the official remote-dom
repository: