Quick experiment with the Slinkity 11ty plugin

Bryan Robinson - Oct 22 '21 - - Dev Community

Ben Holmes (the creator of Slinkity) posted on Smashing Magazine today about the creation of the Slinkity plugin for 11ty. After reading it, I had to give Slinkity a try.

The use case

My son's artwork with a like button above it saying there's 3 likes

My almost-6-year old has a little art website that I set up for him. Originally, it was just HTML. Then he made more art, so I converted to 11ty with a Sanity Studio for image management.

Like any adolescent, there's always a desire for more. He wanted a "like" button to know how many people liked his art. Despite explaining to him that I was really only sharing the site with his grandparents and that he wouldn't get too many likes. I promised I'd implement the feature if he consistently drew pictures for it.

He's done a good job, so I needed to write the feature...

Downside: I had no build process. Upside: A chance to see what folks were using for tiny interactions!

I reached out on Twitter to see what folks thought. Even on a weekend, there were opinions.

From "Roll your own in JS" to "web components" to "petite vue" and more. All great suggestions. I started with very simple JS event handlers, but it was pretty messy code. I moved on to a web component, but, as I usually do, I got frustrated by the feel of web components (I really want them to be awesome).

I want to learn more about AstroJS, so I started re-writing the site in Astro (since there wasn't much functionality yet). It went relatively well, but the data pipeline is still pretty rough around the edges. I was able to get my Sanity content pulling in, but not without some annoyances (that the Astro discord helped me get through, I should add!).

Fast forward to Ben's post and my realization that Slinkity had the same ideas as AstroJS about islands of static content and rehydrated content (which I totally think is the future of the web... and maybe it's the past, too...).

The code

Expect a bigger tutorial on Slinkity coming up, but for now, here's a bit of code for how I implemented a very simple "Like" button for my son's images.

After I set up my Sanity connection, I had an image.html template that set up individual pagination for detail pages for each of the images – much like a blog post template. This is where I wanted the like button.

I kept things VERY simple (it's a site for a 6-year old after all!). Basically, a template to show an image with a caption. Quick aside, the imageUrlFor shortcode comes from my alpha Sanity Image plugin.

---
layout: "base.html"
pagination: 
    data: images
    size: 1
    alias: image
permalink: "{{ image.slug }}/index.html"
---

<div class="container">

    <figure class="stack">
        <img src="{% imageUrlFor image.image, 750 %}">
        <figcaption>{{ image.caption }}</figcaption>
    </figure>

</div>
Enter fullscreen mode Exit fullscreen mode

Next, I needed to install the Slinkity package and change the way I was building and serving my site. What Slinkity does is creates a build process that runs your 11ty instance and then runs a Vite server on the _site directory (or whatever your config file says is your output directory).

npm i --save-dev slinkity
Enter fullscreen mode Exit fullscreen mode
{
  // package.json
  "scripts": {
    // Start changes to slinkity --serve instead of eleventy --serve
    "start": "slinkity --serve",
    // Build changes to slinkity instead of eleventy
    "build": "slinkity"
  },
}
Enter fullscreen mode Exit fullscreen mode

Once that was installed, I was ready to create a React component and insert it into my template.

The component and the react shortcode

To add the component, I needed to create a components directory inside my _includes directory. In that directory, I added a LikeCounter.js file.

This code could probably be refined more, but at its essence it's a component that takes a current like count and an image ID (the Sanity document ID). It uses the count for initial HTML display (statically built during the build). It then fetches an updated count from a serverless function. The serverless function queries Sanity based on the document ID and gets the current count. I also have a serverless function for incrementing the count when a user clicks.

import React, { useEffect, useState } from 'react'

function LikeCounter({likeCount, id}) {
  const [count, setCount] = useState(likeCount)
  useEffect(() => {
    fetch('/.netlify/functions/getLikes', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
            },
            body: JSON.stringify({image: id})
            })
            .then(res => res.json())
            .then(res => {
                setCount(res.count)
            })


  }, [])
  async function handleClick() {
    setCount(count + 1)

    fetch('/.netlify/functions/setLikes', {
          method: 'POST',
          body: JSON.stringify({
              image: id
              })
            })
            .then(res => res.json())
            .then(res => {
                console.log(res)
                setCount(res.count)
            })
}

  return (
    <div>
      <p>This photo has {count} likes ❤️ 
        <button onClick={handleClick}>Add a like</button>
      </p>   
    </div>
  )
}

export default LikeCounter
Enter fullscreen mode Exit fullscreen mode

Once the component is created, I added the component to my template.

---
layout: "base.html"
pagination: 
    data: images
    size: 1
    alias: image
permalink: "{{ image.slug }}/index.html"
---

<div class="container">

    {% react 'components/LikeCounter' 'likeCount' image.count 'id' image._id %}

    <figure class="stack">
        <img src="{% imageUrlFor image.image, 750 %}">
        <figcaption>{{ image.caption }}</figcaption>
    </figure>

</div>
Enter fullscreen mode Exit fullscreen mode

The react shortcode accepts a number of arguments that will be ingested as props. In this case, I pass the likeCount and id.

Slinkity handles the rest.

In about an hour, I had a working React component and a new build pipeline. I got to keep all the things I love about 11ty.

Why not Astro?

I'd originally planned on using this as a way to test and play with AstroJS. I really enjoyed playing with it in this use case, but the data pipeline was a bit rough to deal with for ingesting data from Sanity. I know the Astro team is currently working on the data pipeline, so I'm confident that for use cases like this it'll be a great solution soon.

More than anything Astro felt like a good bridge between the world of 11ty and the world of frameworks like Next. It felt kind of like I was writing Next code next to 11ty code, which was nice.

I think both these advancements are 100% the future. Slinkity gets the advantage of the current 11ty ecosystem and tooling.

Next Steps

Right now Slinkity is just compatible with React, but I know the team is working on integrating with other libraries (and even vanilla JS, which I'm excited about!).

For now, I've got some additional ideas for using Slinkity to add more interactive islands with no need for a large build process, which is super exciting to me.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .