MelonyWidget
Component for rendering individual widgets or component definitions without markdown context.
Import
import { MelonyWidget } from "melony";Basic Usage
MelonyWidget renders component markup directly, without mixing with markdown:
import { MelonyWidget } from "melony";
const widget = `
<card title="Weather">
<text value="Sunny, 72°F" />
<badge label="Clear" variant="success" />
</card>
`;
function Component() {
return <MelonyWidget>{widget}</MelonyWidget>;
}Props
children
string | ComponentDef • Required
Either a string containing Melony component markup, or a ComponentDef object defining the component structure programmatically.
String Format:
<MelonyWidget>
<card title="Hello">
<text value="World" />
</card>
</MelonyWidget>ComponentDef Format:
const componentDef = {
component: "Card",
props: { title: "Hello" },
children: [
{
component: "Text",
props: { value: "World" },
},
],
};
<MelonyWidget>{componentDef}</MelonyWidget>context
Record<string, any> • Optional
Custom data object for template variable substitution.
const context = {
user: { name: "Alice" },
temperature: 72,
};
const widget = `
<card title="{{user.name}}'s Dashboard">
<text value="Current temp: {{temperature}}°F" />
</card>
`;
<MelonyWidget context={context}>{widget}</MelonyWidget>Use Cases
1. Rendering Pure Components
When you have component markup without any markdown text:
const componentMarkup = `
<card title="User Profile">
<row gap="md">
<text value="Name: John Doe" />
<badge label="Active" variant="success" />
</row>
<button label="Edit Profile" action='{"type":"edit"}' />
</card>
`;
<MelonyWidget>{componentMarkup}</MelonyWidget>2. Dynamic Widget Generation
Generate widgets programmatically with context:
function WeatherWidget({ location, temp, condition }) {
const context = { location, temp, condition };
const template = `
<card title="{{location}} Weather">
<row gap="sm">
<text value="{{temp}}°F" size="xl" weight="bold" />
<badge label="{{condition}}" variant="primary" />
</row>
</card>
`;
return <MelonyWidget context={context}>{template}</MelonyWidget>;
}
<WeatherWidget location="SF" temp={72} condition="Sunny" />3. Testing Components
Useful for testing component rendering in isolation:
import { render } from "@testing-library/react";
import { MelonyProvider, MelonyWidget } from "melony";
test("renders button component", () => {
const { getByText } = render(
<MelonyProvider>
<MelonyWidget>
<button label="Click Me" variant="primary" />
</MelonyWidget>
</MelonyProvider>
);
expect(getByText("Click Me")).toBeInTheDocument();
});ComponentDef Format
For programmatic component generation, use the ComponentDef object format:
interface ComponentDef {
component: string; // Component name (e.g., "Card", "Text")
props?: Record<string, any>; // Component props
children?: (ComponentDef | string)[]; // Nested components or text
}
const def: ComponentDef = {
component: "Card",
props: { title: "Weather", padding: "lg" },
children: [
{
component: "Text",
props: { value: "Sunny", size: "xl" },
},
{
component: "Badge",
props: { label: "Clear", variant: "success" },
},
],
};
<MelonyWidget>{def}</MelonyWidget>MelonyWidget vs MelonyMarkdown
Use MelonyWidget when:
- You have pure component markup without markdown
- You're rendering a single, focused widget
- You're building programmatic component definitions
- You're testing components in isolation
Use MelonyMarkdown when:
- You have mixed markdown text and components
- You're rendering AI streaming responses
- You need markdown formatting (headings, lists, etc.)
- You're building chat interfaces
Complete Example
import { MelonyProvider, MelonyWidget } from "melony";
import { useState } from "react";
function WeatherDashboard() {
const [weather] = useState({
location: "San Francisco",
temperature: 72,
condition: "Sunny",
humidity: 65,
});
const handleAction = (action) => {
if (action.type === "refresh") {
console.log("Refreshing weather...");
}
};
const context = {
...weather,
lastUpdated: new Date().toLocaleTimeString(),
};
const widgetTemplate = `
<card title="{{location}} Weather" padding="lg">
<row gap="lg">
<column>
<text value="Temperature" size="sm" color="muted" />
<text value="{{temperature}}°F" size="xl" weight="bold" />
</column>
<column>
<text value="Condition" size="sm" color="muted" />
<badge label="{{condition}}" variant="success" />
</column>
<column>
<text value="Humidity" size="sm" color="muted" />
<text value="{{humidity}}%" weight="medium" />
</column>
</row>
<text value="Last updated: {{lastUpdated}}" size="sm" color="muted" />
<row gap="sm">
<button
label="Refresh"
variant="primary"
action='{"type":"refresh","location":"{{location}}"}'
/>
<button label="Details" variant="outline" />
</row>
</card>
`;
return (
<MelonyProvider onAction={handleAction}>
<MelonyWidget context={context}>
{widgetTemplate}
</MelonyWidget>
</MelonyProvider>
);
}
export default WeatherDashboard;Notes
- Must be used within a
MelonyProviderto access theme and actions - Accepts either string markup or ComponentDef objects
- Context variables work the same as in
MelonyMarkdown - No markdown parsing—only component rendering
- Useful for building reusable widget wrappers
See Also
MelonyMarkdown - For markdown with embedded components
MelonyProvider - Root provider component
Widget Templates - Learn about widget templates
Context System - Learn about context variables