Skip to content

Getting started

  1. Install the package.

    npm install penciled
  2. Call annotate() on the element that holds your content, and import the default styles.

    import { annotate } from 'penciled';
    import 'penciled/styles.css';
    annotate('#article');

    annotate() accepts a CSS selector or an element. Readers can now select text inside it to highlight it in one of five colors, add a note, or click a highlight to change it.

  3. Reload the page. Annotations are saved to localStorage as they change and restored when the page loads. No backend is needed.

penciled/styles.css holds the toolbar, the note editor and the highlight colors. Import it once, from JavaScript as above or from CSS:

@import 'penciled/styles.css';

Its selectors have zero specificity, so your own CSS wins. Only penciled’s buttons and note field carry one attribute, so element resets like Tailwind’s preflight can’t strip them. See Theming and customization.

Annotations are stored under a document id. By default it is the page’s pathname, plus the container’s id when it has one. In development penciled warns about this default, because in single-page apps the URL doesn’t always identify the content. Pass your own id when the same page can show different content:

annotate('#article', { documentId: `post-${post.slug}` });

annotate() returns an instance for reading and changing annotations, listening to events and cleaning up:

const penciled = annotate('#article', {
onCreate: (annotation) => console.log('Highlighted', annotation.quote),
});
penciled.getAll(); // every annotation, in document order
penciled.export(); // { version, documentId, annotations }
penciled.destroy(); // removes highlights, UI and listeners

See Instance for every method.

Importing penciled is safe on the server: nothing touches window or document until annotate() runs. Call annotate() in the browser only, for example in an effect or a client-side script. In React, use the React adapter.