Skip to content

What is a Component?

A component is a reusable message element that supplies both the plain-text body and the formatted HTML body of a message:

  • to_plain_text() returns the plain-text version.
  • render() returns the HTML version.

Pass a component to Context.reply or Room.send with the component keyword:

await ctx.reply(component=table)
# Equivalent when sending directly through a room:
await ctx.room.send(component=table)

See also the Component reference.

Using a Table

matrix.py includes a built-in Table component for displaying labeled fields:

from matrix import Bot, Table

bot = Bot()


@bot.command()
async def user_info(ctx):
    table = Table(title="User Info")

    table.add_field("Name", "Astra")
    table.add_field("Role", "Engineer")

    await ctx.reply(component=table)

Each field is added with add_field:

table.add_field("Name", "Astra")

The first argument is the field name, and the second is its value.

Setting the Column Count

Tables use two columns by default. You can change this with column_count:

table = Table(
    title="Server Info",
    column_count=3,
)

table.add_field("Name", "Example")
table.add_field("Status", "Online")
table.add_field("Users", "42")

Fields are placed into rows using the configured number of columns. An incomplete final row is automatically padded with empty cells. Fields are kept in the order in which they are added.

The column count must be greater than zero:

Table(title="Invalid", column_count=0)
# Raises ValueError

Plain-Text Rendering

Use to_plain_text to render a component without HTML:

table = Table(title="User Info")
table.add_field("Name", "Astra")
table.add_field("Role", "Engineer")

text = table.to_plain_text()

The result is:

User Info
Name: Astra
Role: Engineer

HTML Rendering

Use render to generate the HTML representation:

html = table.render()

Calling str on a table produces the same result:

html = str(table)

Table titles, field names, and field values are automatically HTML-escaped.

For example, a field added with a value of "<online>" is rendered as &lt;online&gt;, so user-provided values cannot be interpreted as HTML markup.

Creating a Custom Component

To create another kind of component, subclass Component and implement both rendering methods:

from html import escape

from matrix.component import Component


class Status(Component):
    def __init__(self, name: str, online: bool):
        self.name = name
        self.online = online

    def to_plain_text(self) -> str:
        state = "online" if self.online else "offline"
        return f"{self.name}: {state}"

    def render(self) -> str:
        state = "online" if self.online else "offline"
        return f"<strong>{escape(self.name)}</strong>: {escape(state)}"

Custom components can be sent in exactly the same way as a Table:

await ctx.reply(component=Status("Astra", online=True))