npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@madebywild/wild-rough-notation

v1.1.0

Published

Create and animate hand-drawn annotations in React

Downloads

467

Readme

wild-rough-notation

Create and animate hand-drawn annotations in React. This package combines two great libraries: rough-notation and react-rough-notation. It was extended to support custom annotations and we added events.

Installation

npm install @madebywild/wild-rough-notation

Usage

Basic Example

import { useState } from 'react'
import { RoughNotation } from '@madebywild/wild-rough-notation'

function App() {
  const [show, setShow] = useState(true)

  return (
    <p>
      This is a <RoughNotation type="highlight" color="yellow" show={show}>highlighted</RoughNotation> word.
    </p>
  )
}

Annotation Types

  • underline - Underline the element
  • underline-jiggly - Wavy underline with non-uniform frequency
  • underline-zigzag - Zigzag underline pattern
  • underline-tick - Underline with a tick mark at the end
  • box - Draw a box around the element
  • circle - Draw a circle around the element
  • highlight - Highlight the element (like a marker)
  • strike-through - Strike through the element
  • crossed-off - Cross off the element with an X
  • bracket - Draw brackets around the element
  • custom-path - Render a custom SVG path
  • custom - Render custom React content (see Custom Annotations below)
  • custom:* - Named custom types (e.g. custom:label, custom:badge) for multiple distinct custom annotations

Group Animations

Use RoughNotationGroup to animate multiple annotations in sequence:

import { useState } from 'react'
import { RoughNotation, RoughNotationGroup } from '@madebywild/wild-rough-notation'

function App() {
  const [show, setShow] = useState(false)

  return (
    <RoughNotationGroup show={show}>
      <p>
        <RoughNotation type="underline" color="red" order={1}>
          First
        </RoughNotation>{' '}
        then{' '}
        <RoughNotation type="circle" color="blue" order={2}>
          second
        </RoughNotation>{' '}
        and finally{' '}
        <RoughNotation type="box" color="green" order={3}>
          third
        </RoughNotation>
      </p>
    </RoughNotationGroup>
  )
}

Props

RoughNotation

| Prop | Type | Default | Description | | --------------------- | ---------------------------------------------------------------- | ---------------- | ------------------------------------------------------- | | type | string | 'underline' | Type of annotation | | show | boolean | false | Whether to show the annotation | | animate | boolean | true | Whether to animate the annotation | | animationDelay | number | 0 | Delay before animation starts (ms) | | animationDuration | number | 800 | Duration of the animation (ms) | | color | string | 'currentColor' | Color of the annotation | | strokeWidth | number | 1 | Width of the annotation stroke | | padding | number \| [number, number] \| [number, number, number, number] | 5 | Padding around the element | | iterations | number | 2 | Number of iterations for the rough effect | | multiline | boolean | false | Handle multiline text | | brackets | 'left' \| 'right' \| 'top' \| 'bottom' \| Array | 'right' | Bracket position(s) for bracket type | | customElement | string | 'span' | HTML element to wrap children | | order | number | - | Order in group animation | | getAnnotationObject | (annotation) => void | - | Callback to get the annotation instance | | customPath | string | - | SVG path string for custom-path type | | customPathViewBox | { width: number; height: number } | - | ViewBox dimensions for scaling custom paths | | customRender | ReactNode | - | Custom content to render for custom / custom:* types | | onAnimationStart | () => void | - | Callback when animation starts | | onAnimationComplete | () => void | - | Callback when animation completes | | onRefresh | () => void | - | Callback when annotation is refreshed (e.g., on resize) |

RoughNotationGroup

| Prop | Type | Default | Description | | ---------- | ----------- | ------- | -------------------------------------------------- | | show | boolean | false | Whether to show all annotations in sequence | | children | ReactNode | - | Child elements containing RoughNotation components |

Custom Annotations

The custom type allows you to render any React content as an annotation. This is useful for custom graphics, icons, or complex decorations that can't be achieved with the built-in types.

import { RoughNotation } from '@madebywild/wild-rough-notation'

function App() {
  return (
    <RoughNotation
      type="custom"
      show={true}
      customRender={
        <div className="absolute inset-0 flex items-center justify-center">
          <svg viewBox="0 0 100 100" className="w-full h-full">
            <circle cx="50" cy="50" r="40" fill="none" stroke="red" strokeWidth="2" />
          </svg>
        </div>
      }
    >
      Custom annotation
    </RoughNotation>
  )
}

The customRender content is rendered inside an absolutely positioned container that matches the dimensions of the annotated element. The annotation automatically handles browser resize events.

Named Custom Types

When you need multiple distinct custom annotations, use the custom: prefix with any name. This avoids hardcoding a fixed set of custom types and lets you distinguish between them in your application logic.

import { RoughNotation, isCustomType } from '@madebywild/wild-rough-notation'

function App() {
  return (
    <>
      <RoughNotation
        type="custom:label"
        show={true}
        customRender={<LabelAnnotation text="New!" />}
      >
        Feature
      </RoughNotation>

      <RoughNotation
        type="custom:badge"
        show={true}
        customRender={<BadgeAnnotation icon="star" />}
      >
        Premium
      </RoughNotation>
    </>
  )
}

Any type matching custom:* behaves identically to custom -- it renders nothing in the core SVG layer and delegates entirely to customRender. The type name is available for you to branch on in your own code.

The exported isCustomType() helper returns true for both 'custom' and any 'custom:*' type:

import { isCustomType } from '@madebywild/wild-rough-notation'

isCustomType('custom')       // true
isCustomType('custom:label') // true
isCustomType('underline')    // false

Custom Path Annotations

The custom-path type lets you use an SVG path string rendered with the rough/hand-drawn style:

<RoughNotation
  type="custom-path"
  show={true}
  customPath="M0.166992 21.9907L183.167 1.49066L163.667 13.4907"
  customPathViewBox={{ width: 184, height: 24 }}
  color="red"
>
  Custom path underline
</RoughNotation>

Advanced Usage

For custom implementations, you can import the core utilities:

import {
  annotate,
  annotationGroup,
  renderAnnotation,
  isCustomType,
  type RoughAnnotationConfig,
  type RoughAnnotation,
} from '@madebywild/wild-rough-notation'

// Create an annotation manually
const element = document.getElementById('my-element')
const annotation = annotate(element, {
  type: 'underline',
  color: 'red',
  animationDuration: 1000,
})

annotation.show()

Publishing

This package uses Changesets for version management and publishing. The package is configured as a private package and will be published to npm with restricted access.

Release Workflow

To release a new version, follow these steps:

# 1. Create a changeset describing your changes
npm run changeset

npm run release

this will ask for your npm OTP code and then publish the package to npm.

Important Notes

  • Private Package: This package is configured with "publishConfig": { "access": "restricted" }, which means it will be published as a private package on npm. Only users/organizations with access can install it.
  • 2FA Required: If your npm account has two-factor authentication enabled, you'll need to provide a one-time password (OTP) when publishing. Get the code from your authenticator app and pass it with the --otp flag.
  • Version Bumping: The npm run version command automatically bumps the version based on your changeset (patch, minor, or major).

License

MIT