Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Widgets

This page demonstrates the MyST widget renderer in myst-theme. Widgets follow the anywidget specification and are added using the {anywidget} directive.

Example widget structure

The following local widget shows off the main API that widgets use within MyST:

anywidget-counter.mjs
/**
 * An anywidget module must define and export a `render` function that uses two arguments:
    - model: Widget state, initialized from the JSON body of the directive.
             It has three methods (get, set, on) to update its state, demo'ed below.
    - el: The DOM element that is outputted. It begins empty and authors create DOM elements inside.
 */
function render({ model, el }) {
  // Inject default styles via a <style> tag so that we can over-ride them with a .css file
  // This gives a user more flexibility than hard-coding styles with `style` attributes on the button element.
  const style = document.createElement('style');
  style.textContent = `
    .counter-button {
      font-size: 16px;
      cursor: pointer;
      border: 2px solid #333;
      color: #333;
      border-radius: 4px;
      padding: 0.4em 0.8em;
      background: #f0f0f0;
    }
  `;
  el.appendChild(style);

  // Create the button element that we'll update
  let btn = document.createElement('button');
  btn.classList.add('counter-button');
  btn.innerHTML = `count is ${model.get('count')}`;

  // Attach a click listener to our element to update its state
  // Use `get` to grab the current widget count
  // Use `set` to set a new widget count
  btn.addEventListener('click', () => {
    model.set('count', model.get('count') + 1);
  });

  // Attach an event listener for when the `count` changes to update our HTML
  model.on('change:count', () => {
    btn.innerHTML = `count is ${model.get('count')}`;
  });

  // By attaching it to the `el`, it will now be displayed on the page
  el.appendChild(btn);
}
export default { render };

Local widget via path

This widget includes its own inline styles, so it looks good without any external stylesheet:

Invalid anywidget directive.
Invalid import URL
/build/anywidget-counter-18bbc1ae96a4d7c1dc5217af5f03c72b.mjs

You can also provide an additional CSS stylesheet via :css: to override or enhance the built-in styles:

Invalid anywidget directive.
Invalid import URL
/build/anywidget-counter-18bbc1ae96a4d7c1dc5217af5f03c72b.mjs

Remote widget via URL

The following loads a widget from the example-js-anywidget repository:

Invalid anywidget directive.
Invalid import URL
/build/4aad6fd05f950c577d6f759e0734bc07.mjs

Security and best practices

Do not modify items outside of the el element

Widgets have full access to the document element of a page, which means they can select, modify, or remove any element on the page. Don’t do this! It will create confusion with React and is generally not a supported workflow, even though it is technically possible.