If you don’t like reading, and want to jump straight into coding, you can use my Starter Template Repo: https://github.com/nienow/sn-extension-template.


This guide will go over the following:

  1. The structure of a note
  2. The API for communicating with Standard Notes
  3. API Implementation Example
  4. Styling your plugin with the chosen theme
  5. JSON descriptor file


Because editor plugins load in an iframe inside Standard Notes, you are free to choose any javascript framework / technology to write a plugin.

The structure of a note

A note contains the following structure:

  "uuid": "ec677a63-5fae-440e-84cd-a62ae7c7d894",
  "content_type": "Note",
  "created_at": "2023-03-26T02:01:59.462Z",
  "updated_at": "2023-03-27T00:18:36.199Z",
  "isMetadataUpdate": false,
  "content": {
    "text": "This is the text content",
    "title": "My Note Title",
    "editorIdentifier": "org.standardnotes.bold-editor",
    "references": [],
    "appData": {
      "org.standardnotes.sn": {
        "locked": false
      "dev.randombits.my-editor": {
        "key": "value"
    "spellcheck": false,
    "preview_plain": "Hello World",
    "preview_html": "<div></div>"

But there are really only 3 important properties that we care about:

  1. text - The content of the note. It is always stored as a string, but we can stringify it if we need to store more complex JSON data.
  2. preview_plain or preview_html - The preview text that is shown in the note list
  3. appData - This is where metadata about the note is stored. Standard Notes stores things like whether the note is locked for editing. And we can store our own metadata under our own key, in JSON format.

The remaining properties are either edited outside of our editor (like the title), or generated by standard notes.

Standard Notes API

An plugin communicates back and forth with Standard Notes to load and save the note. I highly recommend using the API that I wrote: https://github.com/nienow/sn-extension-api, which greatly simplifies the process of creating plugins.

If you want to use one of the official APIs instead, see the article on the ComponentRelay API

Install the API:

npm install sn-extension-api

Basic usage with a text area:

import snApi from "sn-extension-api";

// only call this once - it will establish communication with standard notes

// get notified when note is received from standard notes
snApi.subscribe(() => {
  // set current text into text area for editing
  document.getElementById('my-text-area').value = snApi.text;

document.getElementById('my-text-area').addEventListener('input', (e) => {
  // update text on change - automatically saves to standard notes
  snApi.text = e.target.value;

See the full API documentation

Styling using Themes

Since Standard Notes has several different built-in themes, and the option to install custom themes, we need our plugin to use these themes. We first need to import the base theme variables into our root CSS file:

@import 'sn-extension-api/dist/sn.min.css';

This will include all the default CSS theme variables. Here is a condensed list of the variables you might use:


You can check out the full list of variables.

When you are styling your editor, you will want to use the theme variables for anything involving color and font-size:

body {
  background-color: var(--sn-stylekit-background-color);
  color: var(--sn-stylekit-foreground-color);
  font-family: var(--sn-stylekit-editor-font-family);
  font-size: var(--sn-stylekit-font-size-editor);

button {
  background-color: var(--sn-stylekit-contrast-background-color);

textarea {
  border: 1px solid var(--sn-stylekit-border-color);

When you use these variables, and the user switches themes, your editor will automatically change too!

JSON Descriptor File

You will need to create a descriptor file, which is how people will install your plugin.

The file is normally hosted at the same url of your application at /ext.json, but it doesn’t have to be. Here is an example file:

  "identifier": "dev.randombits.my-editor",
  "name": "My Editor",
  "description": "My cool editor",
  "content_type": "SN|Component",
  "area": "editor-editor",
  "version": "1.0.0",
  "url": "https://nienow.github.io/sn-extension-template/",
  "download_url": "https://nienow.github.io/sn-extension-template/latest.zip",
  "latest_url": "https://nienow.github.io/sn-extension-template/package.json"

identifier - A unique identifier, usually in reverse domain format

name - The name that shows up under the editor picker

description - This shows up under the list of installed plugins

content_type - “SN|Component” for everything but a theme

area - “editor-editor” is the main editor area

version - The version of your plugin.

url - The url of your plugin HTML page

download_url (optional) - The url for Standard Notes to download the full plugin distribution (HTML page, scripts, stylesheets, etc). Only the desktop version of the app uses this. It downloads the whole distribution so that you can use the app offline. However, this value is optional, because the desktop version will simply fallback to the using the regular “url” value if it is not specified.

latest_url (optional) - The url for the desktop app to check whether it needs to download a new version of the plugin. It is only required if “download_url” is specified. The file it points at should be a json file containing a “version” property. The package.json or the ext.json file could be used for this, since they both contain the version.

Deploy / Hosting

The most common way to host a plugin is through Github Pages. Not only is it the easiest way, but also users will trust your plugin if it is hosted next to your source code.

Starter Template Repo

I’ve created a complete starter template, which includes a demo/testing environment: https://github.com/nienow/sn-extension-template