♻️ Very Simple Components – When even Alpinejs or Petite-Vue would be too much

I’d like to share a library of mine that I’ve been using in production for quite some time now:
:recycle: Very Simple Components – When even Alpinejs or Petite-Vue would be too much

What it does is simply provide a convenient way to attach JavaScript/TypeScript to the DOM. It takes some of the verbosity out of vanilla JS, and keeps everything organized as components.

An example: You want a simple gallery component with slides and prev/next buttons

// components/gallery.js

import { registerComponent, defineOptions } from '@very-simple/components'

const options = defineOptions({
  props: { loop: Boolean }
})

registerComponent('gallery', options, ({ el, props, refs, refsAll }) => {
  // Props are read from el's dataset and are automatically converted to the correct
  // type. Default values are also possible, see documentation.
  const { loop } = props

  // Multiple HTML elements can have the same `ref` name. They will be
  // grouped in `refsAll` ...
  const { slides } = refsAll

  // ... whereas `refs` only stores a single element per name.
  const { prev, next } = refs

  let currentIndex = 0
  const maxIndex = slides.length - 1

  const selectSlide = index => {
    if (!loop && (index < 0 || index > maxIndex)) return

    currentIndex = index < 0 ? maxIndex : index > maxIndex ? 0 : index
    slides.forEach((el, index) => (el.hidden = index !== currentIndex))
  }

  // Add event listeners.
  prev.addEventListener('click', () => selectSlide(currentIndex - 1))
  next.addEventListener('click', () => selectSlide(currentIndex + 1))

  // Show the first slide.
  selectSlide(currentIndex)
})
<!-- template.php -->

<!--  For better reusability this would go into a snippet -->
<div data-simple-component="gallery" data-loop="true">
  <div data-ref="slides">A</div>
  <div data-ref="slides">B</div>
  <div data-ref="slides">C</div>
  <button data-ref="prev">Prev</button>
  <button data-ref="next">Next</button>
</div>

<script type="module">
  import { mountComponents } from '@very-simple/components'
  // We only have to import the component, it will register itself.
  import './components/gallery.js'

  // This will look for any elements with a `data-simple-component` attribute and
  // mount the corresponding component.
  mountComponents()
</script>

So there’s not a lot of magic here, and no reactivity like in most other frontend frameworks. It’s just some helpers and quality-of-life features that make working with vanilla JS a little nicer. And it is super small, less than 1kb (minify and gzip)!

It provides some additional features not shown in the example, like fully typed native component events and exposing methods on a component’s HTML element (e.g: myMenu.$component.open()).

Also you get great auto-completion in your IDE and strict type checking when using TypeScript. The library hasn’t reached 1.0.0 yet, so things may change, but it’s already production-ready.

1 Like

I changed the category, otherwise, every frontend library would end up as plugin.

Thanks! I thougth about the Kirby 4 solution category, but didn’t use it since the library isn’t specific to any Kirby version.