Skip to the content
Michael V. ColiannaAuthor / Web Developer

Juniper & Elm Quilts

Michael's wife's website, previously on Squarespace, converted to a Nuxt 3 app, and using Storyblok.
Back to Work

My wife is a very accomplished and talented quilter. A while back she had a need for a website and I was busy with work things so she signed up for Squarespace. Though it got the job done, the service is expensive.

After some looking around, plus some changes at work, I decided to finally convert her site over to something closer to free. I demonstrated a couple of headless CMS options for her and she chose Storyblok. I then set about making the various elements she would need to replicate the content from Squarespace. Rather than highlight the design (since I did a slimmed-down, more accessible copy of the existing design) I figured it'd be nicer to showcase the backend.

Block library
The Storyblok block library, showing the various content elements like the mantel and inline aligned images.
Aligned Image component
The Storyblok editor, showing an entry about a throw size quilt on the left, and the fields on the right, demonstrating how the "aligned image" component functions.

Those two images should have ALT text but if not, one is the list of all the Storyblok components I made and the other is Storyblok's half-WYSIWYG editor – you edit content in a pane on the right and the changes preview in a pane on the left.

Newsletter component
The component for the newsletter sign up, showing a configurable title, copy text, and background image.

That image showcases the newsletter signup component, which is a sort of hybrid and provides a good segue to the backend code. I built my site using Gatsby so I wanted to have something that demonstrated my non-React code. I chose to utilize Nuxt 3 for the backend code and frontend display.

Newsletter code
The backend code for the newsletter signup, showing the use of the Fetch API (via Nuxt's "useFetch" composable).
Custom "getStory" composable
The code for my custom "getStory" composable, which handles the main call to Storyblok's API so I don't have duplicate code across views.
Blog template code
My setup code for the blog template, showing that I use my getStory composable to fetch the appropriate entry and then make use of Nuxt's "useHead" composable to set SEO data.
Custom "StoryblokImage" component
My custom "StoryblokImage" component, which I use to render a NuxtImg with the appropriate URL for the image service.

Those images cover some key elements in the code:

  1. The newsletter component, which utilizes the "useFetch" composable and interacts with Sendinblue's API to create a double opt-in contact, and displays appropriate reactive statuses.

  2. A custom composable I wrote to fetch stories from Storyblok, as I found I was rewriting this same code in multiple views. It takes all current mutations into account for delivering the different content types.

  3. The setup script for the blog page template, which uses my custom composable. It also makes use of the "useHead" composable to override the default SEO information. (Note that the "meta" constant also defines some items using other custom composables.)

  4. A custom component I wrote for displaying images from Storyblok. Notably, the Nuxt Image module doesn't know to strip the beginning of the service endpoint, so I am doing that manually. I did so because passing the normal URL doesn't make use of the service so it doesn't output optimized images or sizes.

Nuxt config
The config file for the Nuxt project, showing the default SEO info and some other standard configuration options.

That last image is the Nuxt config file, which just shows some defaults I set up for SEO and how I configured the CSS/other items.

Just like my personal website, the source code is on GitHub and is publically viewable.