〈 Back

My experience with Wisp.blog - part 3

Mon Sep 22 2025

In part 1 of this series I've decided to start a blog (to document the process of starting a blog) and in part 2 I describe the stack I've decided to use. This time, I have actually started to show the blog on the website.

API or SDK?

This led me to the first decision I had to make, would I use the REST API, or the Javascript SDK? I did choose the SDK, it seems to have all the functions you'd need and is less complex to implement. It is nice to have the ability to use the REST API in case you'd have a separate backend running on another language.

For convenience I'll continue to refer the point from which I fetch data as API, since both return the same data, and the term API covers both of them.

Implementing the SDK was no issue at all, out of the box it works without problems. It didn't take long before I could start displaying my first post on a barren overview page. The response of the API was clear and structured. It returns two objects: the posts and the pagination data. Everything is logically structured and a breeze to work with.

So next up was the article page itself.

Dangerous HTML

This is where I discovered the first (and only one so far) issue with Wisp. It will return the entire blog content as a single string containing HTML tags. This is easy to quickly render it, but could introduce XSS issues when it comes from an untrusted source. The React framework makes very clear doing this isn't considered a best practice, as you need to use a property with a warning in it's name to set the content in a roundabout way.

The basic component to render a blog post would look something like this.

import { buildWispClient } from '@wisp-cms/client';

interface BlogPostProps extends React.ComponentProps<'article'> {
    slug: string;
}

export async function BlogPost({ slug, ...props }: BlogPostProps) {
    const wispClient = buildWispClient({
        baseUrl: 'https://www.wisp.blog',
        blogId: process.env.NEXT_PUBLIC_BLOG_ID!
    });
    const { post } = await wispClient.getPost(slug);

    if (!post) {
        throw 'Blogpost not found';
    }

    return (
        <article className="prose dark:prose-invert" {...props}>
            <h1>{post.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.content }}></div>
        </article>
    );
}

While this isn't ideal, as long as the HTML comes from a trusted source, it shouldn't be too big of an issue. And you can also make this more secure using the sanatize-html package.

Though, there is one thing I don't fully understand. The editor of Wisp is clearly based on Markdown, and I wouldn't be surprised if it would actually save the content as Markdown, to return HTML when requested through the API. Why don't we have a parameter we can add, to either request HTML or Markdown as a response? With a Markdown response we can at least have some control over what HTML we render.

What's next?

There are a couple of things still on my (imaginary) roadmap: I want to test the functionality of Wisp's comment feature, I might have to implement some SEO for this blog to actually be read, and it won't hurt to work on the UI/UX of this website. Another thing that's still on the horizon is pagination on the overview page. Though pagination won't add much value if I only have a few posts, so I'll add that later.

Powered by wisp

Comments

No comments yet, be the first to comment!