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

@ripl/vue

v1.4.0

Published

Declarative Vue 3 components and compositions for Ripl

Readme

@ripl/vue

npm license size

Declarative Vue 3 components for Ripl. Describe a scene graph as a template, bind props to element state, and let v-if and v-for drive the graph.

Features

  • Every built-in element as a component<ripl-arc>, <ripl-circle>, <ripl-ellipse>, <ripl-image>, <ripl-line>, <ripl-path>, <ripl-polygon>, <ripl-polyline>, <ripl-rect>, <ripl-text> and <ripl-group>, each typed with its own state properties.
  • Three levels of engine, all optional — a context alone paints; add <ripl-scene> for a hoisted graph and z-ordering; add <ripl-renderer> for an animation loop and transitions.
  • <ripl-transition> — enter, update and leave phases with per-element staggering, following Vue's own enter-from / leave-to model.
  • Pointer events as Vue listeners@click, @mouseenter, @drag and the rest, subscribed only when you bind them so hit testing stays accurate.
  • Compositions for the imperative escape hatchuseRiplContext, useRiplScene, useRiplRenderer and useRiplElement.
  • Strict TypeScript, tree-shakable, SSR-safe.

Installation

# npm
npm install @ripl/vue

# yarn
yarn add @ripl/vue

# pnpm
pnpm add @ripl/vue

vue (3.5 or later) is a peer dependency you already have. @ripl/core, @ripl/web, @ripl/dom and @ripl/utilities arrive as dependencies of this package; you never install them yourself.

This package targets @ripl/web, i.e. Canvas 2D. To render through another backend, build the context yourself and pass it in via the context prop on <ripl-context>.

Quick start

Register the components globally:

import {
    createRipl,
} from '@ripl/vue';

import {
    createApp,
} from 'vue';

import App from './app.vue';

createApp(App).use(createRipl()).mount('#app');

Then describe a scene. Give <ripl-context> a size, since the canvas fills it:

<template>
    <ripl-context style="width: 400px; height: 300px">
        <ripl-scene>
            <ripl-renderer>
                <ripl-transition
                    :enter="{ duration: 400, state: { opacity: 0, radius: 0 } }"
                    :update="{ duration: 250 }"
                    :leave="{ duration: 200, state: { opacity: 0 } }"
                >
                    <ripl-circle
                        v-for="item in items"
                        :key="item.id"
                        :cx="item.x"
                        :cy="item.y"
                        :radius="item.radius"
                        fill="#1e6978"
                        @click="select(item)"
                    />
                </ripl-transition>
            </ripl-renderer>
        </ripl-scene>
    </ripl-context>
</template>

Components can equally be imported one at a time, in which case the plugin is unnecessary.

The three tiers

Each level adds capability, and every element picks up the highest one above it:

| Template | What you get | | --- | --- | | <ripl-context> | Elements paint directly. Pointer events and hit testing work. | | + <ripl-scene> | A hoisted, flat instruction stream: z-ordering, group clipping, efficient large graphs. | | + <ripl-renderer> | An animation loop, and <ripl-transition>. |

Transitions

enter is the state an element animates from; leave is the state it animates to; update is how a prop change animates. Each takes an options object or a factory called per element, which is what makes staggering work:

<template>
    <ripl-transition
        :enter="(element, index, length) => ({
            duration: 400,
            delay: (index / length) * 200,
            state: { opacity: 0 },
        })"
    >
        <ripl-rect v-for="bar in bars" :key="bar.id" v-bind="bar" />
    </ripl-transition>
</template>

An enter phase can reference a property the template never binds: the target is read off the element before the enter state is applied, so fading in from { opacity: 0 } recovers a target of 1 from the element's inherited or default state.

loop repeats a phase: true restarts it, 'alternate' plays it back and forth. A looping phase never completes, so its onComplete never fires and the renderer cannot idle while one runs; it is cancelled when its element leaves, and ignored on the leave phase, which has to finish in order to destroy the element.

Compositions

import {
    useRiplContext,
    useRiplElement,
    useRiplRenderer,
    useRiplScene,
} from '@ripl/vue';

const context = useRiplContext();
const scene = useRiplScene();
const renderer = useRiplRenderer();
const element = useRiplElement();

Providers construct during setup(), so these already resolve in a descendant's own setup() with no watching required. They are undefined outside a provider, and during server rendering.

A template ref on any of the components resolves to the Ripl object it wraps, typed as that object:

<template>
    <ripl-context ref="context">
        <ripl-circle ref="circle" :cx="50" :cy="50" :radius="20" />
    </ripl-context>
</template>

Notes

  • A prop you do not bind is never written, so Ripl's own defaults and a group's cascading state survive. Changing a bound prop back to undefined likewise leaves the last value in place.
  • Props are compared by identity, so an inline :data="{ ... }" or :line-dash="[4, 2]" re-applies on every parent render. Hoist those to a computed. class is normalised first, so every binding form is stable.

Extending

@ripl/vue exports the pieces it is built from, so a sibling adapter can wrap a different kind of Ripl object without re-implementing the machinery. @ripl/vue-3d and @ripl/vue-charts are built this way.

| Export | Use | | --- | --- | | defineRiplElement, elementFactory | Wrap anything that extends Element as a component. | | useElementProps | Construct an object from bound props and keep it in sync with them. | | useForwardedEvents | Forward a bus's own $events to Vue listeners, subscribing only to bound ones. | | useExposedInstance | Make a template ref resolve to the Ripl object rather than a Vue proxy. | | registerComponents | Register components on an app, skipping names already taken. | | RIPL_CONTEXT, RIPL_SCENE, RIPL_RENDERER, RIPL_PARENT, RIPL_ELEMENT, RIPL_TREE, RIPL_TRANSITION | The injection keys the components provide. | | readBoundProps, collectChangedProps, partitionProps, applyState, applyFields | The prop pipeline. |

Two contracts a sibling adapter depends on:

  • This package owns the @ripl/web import, and with it the platform factory: requestAnimationFrame, devicePixelRatio, getDefaultState and measureText. A sibling adapter inherits that through its dependency on @ripl/vue and should not add an @ripl/web import of its own, because a bare side-effect import inside a sideEffects: false package can be tree-shaken away, whereas the value imports here cannot.
  • The injection keys are registry symbols (Symbol.for), so two copies of this module, which the standalone IIFE builds produce, still resolve to the same key.

Plugins compose in any order: createRipl3D() and createRiplCharts() install the core components themselves, and registering a name twice is a no-op.

Documentation

Full documentation lives at ripl.run.

License

MIT