tinycms Documentation

A handy guide on how to get up and running

Rahul Singh
Rahul
May 2024 • Docs
cms blog sanity sanity.io headless cms

Component Overview

Title: Takes a single text input to create a title
Paragraph: Uses HTML markup to generate paragraphs
List: Uses HTML markup, using the 'li' tag is encouraged
Image: Takes text for subtitle, HTML for a paragraph, image link (source) and alt tag to display an image with text

Quickstart

1. Create a blog using the editor

2. Copy the JSON data and add it to your database

3. Create components in your app for each component_type in the blog

4. Fetch the data and loop through the elements in the content array

5. Using if or switch statements on the component_type, render the correct component

Heads up for Tailwind users

If you would like to use Tailwind classes in your blog that are not already in use within your web app you will need to safelist them. This is due to Tailwind's treeshaking. Any classes not in use will be removed from your bundle and as a result will not render when you inject HTML.

Svelte - Blog Components

Add the following components to your components directory.
typescript
// Within script tag of Head.svelte

export let blog: any
html
<h1 class="text-5xl font-bold text-center break-words mb-4">{blog.title}</h1>
<p class="text-md opacity-60 mb-4 break-words">{blog.description}</p>
<div class="flex items-center justify-between mb-6 w-full sm:w-3/4">
    <div class="flex gap-1 items-center">
        <img class="w-10 h-10 rounded-full" src={blog.author_image} alt={blog.author_alt}>
        <div class="felx flex-col text-left">
            <div class="text-sm font-semibold opacity-60">{blog.author}</div>
                <div class="text-xs opacity-60">{blog.date} • {blog.category}</div>
            </div>
        </div>  
    </div>
<img src={blog.image_url} alt={blog.image_alt} class="w-full mb-6 sm:w-3/4 rounded-xl">

Svelte - Serving the blog

Create a route that can handle slugs and then add a +page.ts which will be used to fetch the data for the blog.
typescript
// Within /blogs/[slug]/+page.ts

export async function load({ params }) {
    const slug = params.slug;
    /* 
    In this case there is a table in the database called "blogs".
    The JSON data is in a column called "content".
    And a column called "slug" that will be used to match the URL parameter to the content.
    */

    // You will need to replace this with your own query
    const { data, error } = await supabase.from('blogs').select('content').eq('slug', slug).single();
    if (error) {
        throw error;
    }
    else {
        return data;
    }
}
Next make a +page.svelte in the slug folder.
typescript
// Within script tag of +page.svelte

// Import Components (LOCATIONS WILL VARY DEPENDING ON YOUR SETUP)
import Head from '../../components/blog/Head.svelte';
import Title from '../../components/blog/Title.svelte';
import Paragraph from '../../components/blog/Paragraph.svelte';
import List from '../../components/blog/List.svelte';
import Image from '../../components/blog/Image.svelte';

// Get the data from the load function
export let data;
let blog = data['content'];
html
<div class="w-full sm:w-3/4">
<!-- Blog Head -->
<div class="flex flex-col items-center justify-center">
    <Head blog={blog} />
</div>
<!-- Render each content element based on type -->
{#each blog.content as content}
    {#if content.component_type === 'title'}
        <Title text={content.props.Text} />
    {:else if content.component_type === 'paragraph'}
        <Paragraph text={content.props.HTML} />
    {:else if content.component_type === 'list'}
        <List text={content.props.HTML} type={content.props.Type} />
    {:else if content.component_type === 'image'}
        <Image src={content.props.Source} alt={content.props.Alt} subtitle={content.props.Subtitle} text={content.props.HTML} direction={content.props.Type} />
    {/if}
{/each}
<div>