Qreacto

Run standalone React components in your Quarto project!

Getting started

Create your component

function MyComponent() {    
    const [text, setText] = React.useState(['Click me'])
    return (
      <div onClick={() => setText((previous) => [previous, ...[' and me']])}>
        <p>{text.map(value => <span>{value}</span>)} </p>
      </div>
    );
  }

export default MyComponent

add it to your markdown file with {{< react MyComponent >}}

and see the results:

Add styles

Create your component, with the styles imported

import './style.css';
function MyStyledComponent() {    
    const [text, setText] = React.useState([])
    return (
      <div onClick={() => setText((previous) => [...previous, ' and this one'])}>
        <div className='styled-component-container'>
          <span className='wrapper-for-other-component'>Click this one</span> 
          {text.map(value => <span className='wrapper-for-other-component'>{value}</span>)} 
        </div>
      </div>
    );
  }

export default MyStyledComponent

add it to your markdown file with {{< react MyStyledComponent >}}

and see the results:

Import local scripts

Create your component, with the scripts imported

import './style.css';
import getRandomEmoji from './emojiservice'; // we are importing a script here
function EmojiComponent() {    
    const [text, setText] = React.useState([getRandomEmoji()])
    return (
      <div onClick={() => setText((previous) => [...previous, getRandomEmoji()])}>
        <div className='styled-component-container'>          
          {text.map(value => <span className='wrapper-for-other-component'>{value}</span>)} 
        </div>
      </div>
    );
  }

export default EmojiComponent

add it to your markdown file with {{< react EmojiComponent >}}

and see the results:

Import thirdparty scripts

Create your component, with thirdparty scripts imported.

Any CDN that supports ESM packages can be used, like jsDelivr

import './style.css';
import * as randomWords from 'https://cdn.jsdelivr.net/npm/random-words/+esm'; // we are importing a thirdparty script here
function RandomWordsComponent() {
    const [text, setText] = React.useState([randomWords.generate({ maxLength: 8 })])
    return (
      <div onClick={() => setText((previous) => [...previous, randomWords.generate({ maxLength: 8 })])}>
        <div className='styled-component-container'>          
          {text.map(value => <span className='wrapper-for-other-component'>{value}</span>)} 
        </div>
      </div>
    );
  }

export default RandomWordsComponent

add it to your markdown file with {{< react RandomWordsComponent >}}

and see the results:

Nesting components

Create your component with the nested components imported and in the jsx

import RandomWordsComponent from "./RandomWordsComponent";
import EmojiComponent from "./EmojiComponent";

function MyNestedComponent() {
    return (
      <div>
        <RandomWordsComponent/>
        <EmojiComponent/>
      </div>
    );
  }

export default MyNestedComponent

add it to your markdown file with {{< react MyNestedComponent >}} and see the results:

Using environment variables

Add your environment variables as specified on the official documentation Qreacto will pick up environment variables that begin with QREACTO_ They can then be accessed with process.env....โ€™โ€™โ€™

The enviornment file:

QREACTO_FOO=bar
QREACTO_TEST=Very true

Results in:

 {
  env: { 
      QREACTO_FOO:"bar"
      QREACTO_TEST:"Very true"
  }
 }

and can be accessed with:

 console.log(process.env.QREACTO_FOO)
 console.log(process.env.QREACTO_TEST)

Note Qreacto only looks for _environment files, not iterations of this file (eg _environment-dev, _environment-prod) ## Configuring

You can (optionally) specify the location for your components and resources

    react:
        components: _components
        resources: _resources

Gotchas

  • Dont import react in your file import React from 'react'
  • components should be placed in the _components/ folder
  • styles and supporting scripts should be placed in the _components/ folder
  • Changes to your component will not refresh quarto, quarto preview or quarto render will need to be called.
  • Typescript is supported but is limited to babels transpilation support of TS. As such, it may not have the latest features you would expect
  • Named imports are not currently supported import { NamedButton } from './Button'; will not work but import Button from './Button' will.
  • Deep imports are not currently supported. components should be placed in the _components/ folder.
  • Works with md and qmd other extensions might work but have not been tested.