Hyperion
JSX

JSX

You've probably built an embed with discord.js before: new EmbedBuilder(), then .setTitle(), .addFields(), and so on, one call after another. It works, but once a message has a few parts, the shape gets hard to see. @hyperion-js/jsx lets you write the message the way it looks, then turns it into the same builders you'd have made by hand.

import { Bold, Embed, renderEmbed } from "@hyperion-js/jsx";

const embed = renderEmbed(
    <Embed color={0x57f287}>
        <Embed.Title>Todo created</Embed.Title>
        <Embed.Description>
            <Bold>Ready</Bold> for review
        </Embed.Description>
    </Embed>,
);

renderEmbed() hands back an ordinary EmbedBuilder, so sending it is plain discord.js: await interaction.reply({ embeds: [embed] }). The package doesn't need React and doesn't depend on @hyperion-js/core, so everything in this guide works with any discord.js bot. If you do use Hyperion, see JSX with Hyperion for how the two fit together.

Setup

You'll need Node.js 24.17 or newer. Install the package with the @discordjs/builders prerelease it was built against:

npm install @hyperion-js/jsx@beta @discordjs/[email protected]

Then tell TypeScript to use this package's JSX runtime:

{
    "compilerOptions": {
        "jsx": "react-jsx",
        "jsxImportSource": "@hyperion-js/jsx"
    },
    "include": ["src/**/*.ts", "src/**/*.tsx"]
}

Put your views in .tsx files and run them with a runner such as tsx, or compile them for Node. (The option says react-jsx, but that's only the name of the transform; React isn't involved.)

What are you sending?

How rendering works

A view is just a function that returns JSX, and it runs again every time you render. Two things follow from that:

  • Views are synchronous, so fetch your data first and pass it in as props.
  • Every render gives you brand-new builders, so personalising one reply can't leak into another.

Arrays, fragments, and conditionals work the way you'd expect. Use <Br /> for a line break, and {" "} when you need a space between two elements.

When something's wrong, you get a JSXRenderError that points at the exact spot in your view. The underlying discord.js error is attached as cause.

Watch out

The cause can contain the values you passed in, so be careful about logging it.

The authoring reference has the full rules.

On this page