Observable Framework 1.9.0 GitHub️ 2.1k

JSX

React is a popular and powerful library for building interactive interfaces. React is typically written in JSX, an extension of JavaScript that allows HTML-like markup. To use JSX and React, declare a JSX fenced code block (```jsx). For example, to define a Greeting component that accepts a subject prop:

```jsx
function Greeting({subject}) {
  return <div>Hello, <b>{subject}</b>!</div>
}
```

Then call the built-in display function to render content:

display(<Greeting subject="JSX" />);

You can combine React with Framework’s built-in reactivity by passing reactive values as props. Try changing the name below.

display(<Greeting subject={name || "anonymous"} />);
const name = view(Inputs.text({label: "Name", placeholder: "Anonymous"}));

You can use hooks such as useState, useEffect, and useRef. The Counter component below counts the number of times you click the button.

function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      You clicked {count} times
    </button>
  );
}
display(<Counter />);

React is available by default as React in Markdown, but you can import it explicitly like so:

import * as React from "npm:react";

If you prefer, you can import specific symbols, such as hooks:

import {useState} from "npm:react";

React DOM is also available as ReactDOM in Markdown, or can be imported as:

import * as ReactDOM from "npm:react-dom";

You can define components in JSX modules. For example, if this were components/Card.jsx:

export function Card({title, children} = {}) {
  return (
    <div className="card">
      {title ? <h2>{title}</h2> : null}
      {children}
    </div>
  );
}

You could then import the Card component as:

import {Card} from "./components/Card.js";

Use the .js file extension when importing JSX (.jsx) modules; JSX is transpiled to JavaScript during build.

And, as before, you can render a card using the display function:

display(<Card title="A test of cards">If you can read this, success!</Card>);

Within a JSX fenced code block, the display function behaves a bit differently from a JavaScript fenced code block or inline expression: it replaces the previously-displayed content, if any. In addition, JSX fenced code blocks do not support implicit display; content can only be displayed explicitly.

In the future we intend to support other JSX-compatible frameworks, such as Preact. We are also working on server-side rendering with client-side hydration; please upvote #931 if you are interested in this feature.

Inline expressions

JSX is not currently supported in inline expression ${…}; only JavaScript is allowed in inline expressions. However, you can declare a detached root using createRoot:

const node = document.createElement("SPAN");
const root = ReactDOM.createRoot(node);

Then use a JSX code block to render the desired content into the root:

root.render(<>Hello, <i>{name || "anonymous"}</i>!</>);

Lastly, interpolate the root into the desired location with an inline expression:

Rendering into an inline expression

<div class="card">
  <h2>Rendering into an inline expression</h2>
  ${node}
</div>