sveltekit-sanity-daisyui-template Svelte Themes

Sveltekit Sanity Daisyui Template

A barebones SvelteKit starter template for building a blog powered by Sanity CMS and styled with daisyUI.

sveltekit-sanity-daisyui-template

A barebones SvelteKit starter template for building a blog powered by Sanity CMS and styled with daisyUI.

Just a heads-up: this template includes Tailwind CSS and daisyUI, but they haven't really been used much yet. So feel free to have fun customizing and giving your site a complete makeover from the ground up!

Video tutorial

  • Coming soon.

Getting started

To get up and running with this template, you should be familiar with SvelteKit. If you're new to it, the official documentation is a great place to start:

Once you're comfortable, clone this template and customize it to fit your needs.

Important places to customize

Look for TODO: comments throughout the codebase to identify areas that need your attention. Below are the key files and folders you’ll likely want to update:

  1. src/lib/index.ts – Basic site information (e.g., title, description, author).
  2. src/lib/components/MetaHome.svelte – Metadata configuration for your home page.
  3. static/ – Your site's favicon and other identity-defining images.
  4. .env – Environment variables (e.g., API keys, project IDs).

Outcome example

Here's a finished website made with this template to give you some inspiration for your own site makeover.

Sanity studio setup

Install a dependencies

npm i easymde sanity-plugin-markdown

A sample of post schema

//  schemaTypes/postType.ts
import { defineField, defineType } from 'sanity'

export const postType = defineType({
    name: 'post',
    title: 'Post',
    type: 'document',
    fields: [
        defineField({
            name: 'title',
            type: 'string',
            validation: (rule) => rule.required(),
        }),
        defineField({
            name: 'slug',
            type: 'slug',
            options: { source: 'title' },
            validation: (rule) => rule.required(),
        }),
        defineField({
            name: 'publishedAt',
            type: 'datetime',
            initialValue: () => new Date().toISOString(),
            validation: (rule) => rule.required(),
        }),
        defineField({
            name: 'image',
            type: 'image',
        }),
        defineField({
            name: 'video',
            type: 'file',
        }),
        defineField({
            name: 'excerpt',
            type: 'string',
        }),
        defineField({
            name: 'tags',
            type: 'array',
            of: [{ type: 'string' }]
        }),
        defineField({
            name: 'categories',
            type: 'array',
            of: [{ type: 'string' }]
        }),
        defineField({
            name: 'draft',
            type: 'boolean',
            initialValue: false
        }),
        defineField({
            name: 'readingTime',
            type: 'number',
        }),
        defineField({
            name: 'body',
            type: 'markdown',
        }),
    ],
})

Sanity query cheat sheet

Paginated posts (Excludes drafts)

*[_type == 'post' && !draft] | order(_id)[0...5] {
  _id,
  title,
  "slug": slug.current,
  publishedAt,
  "imageUrl": image.asset->url,
  excerpt,
  tags,
  categories,
  draft,
  readingTime,
  body,
  headings,
  "videoUrl": video.asset->url
}

Single post by slug

*[_type == 'post' && slug.current == $slug] | order(_id)[0...1] {
  _id,
  title,
  "slug": slug.current,
  publishedAt,
  "imageUrl": image.asset->url,
  excerpt,
  tags,
  categories,
  draft,
  readingTime,
  body,
  headings,
  "videoUrl": video.asset->url
}

Tagged posts

*[_type == 'post' && !draft && $tagged in tags] | order(_id)[0...5] {
  _id,
  title,
  "slug": slug.current,
  publishedAt,
  "imageUrl": image.asset->url,
  excerpt,
  tags,
  categories,
  draft,
  readingTime,
  body,
  headings,
  "videoUrl": video.asset->url
}

💡 Tips:

  • Replace 0...5 with dynamic values for pagination.
  • Always use "slug": slug.current to avoid nested objects in your output.

Tested query URL at Fri 2 May

# Paginated posts
https://9fp52ven.api.sanity.io/v2025-05-02/data/query/production?query=*%5B_type+%3D%3D+%27post%27+%26%26+%21draft%5D+%7C+order%28_id%29%5B0...5%5D+%7B%0A++_id%2C%0A++title%2C%0A++%22slug%22%3A+slug.current%2C%0A++publishedAt%2C%0A++%22imageUrl%22%3A+image.asset-%3Eurl%2C%0A++excerpt%2C%0A++tags%2C%0A++categories%2C%0A++draft%2C%0A++readingTime%2C%0A++body%2C%0A++headings%2C%0A++%22videoUrl%22%3A+video.asset-%3Eurl%0A%7D%0A&perspective=drafts
# Post by slug
https://9fp52ven.api.sanity.io/v2025-05-02/data/query/production?query=*%5B_type+%3D%3D+%27post%27+%26%26+slug.current+%3D%3D+%24slug%5D+%7C+order%28_id%29%5B0...1%5D+%7B%0A++_id%2C%0A++title%2C%0A++%22slug%22%3A+slug.current%2C%0A++publishedAt%2C%0A++%22imageUrl%22%3A+image.asset-%3Eurl%2C%0A++excerpt%2C%0A++tags%2C%0A++categories%2C%0A++draft%2C%0A++readingTime%2C%0A++body%2C%0A++headings%2C%0A++%22videoUrl%22%3A+video.asset-%3Eurl%0A%7D%0A&%24slug=%22less-code-with-riverpod%22&perspective=drafts
# Tags
https://9fp52ven.api.sanity.io/v2025-05-02/data/query/production?query=*%5B_type+%3D%3D+%27post%27+%26%26+%21draft+%26%26+%24tagged+in+tags%5D+%7C+order%28_id%29%5B0...5%5D+%7B%0A++_id%2C%0A++title%2C%0A++%22slug%22%3A+slug.current%2C%0A++publishedAt%2C%0A++%22imageUrl%22%3A+image.asset-%3Eurl%2C%0A++excerpt%2C%0A++tags%2C%0A++categories%2C%0A++draft%2C%0A++readingTime%2C%0A++body%2C%0A++headings%2C%0A++%22videoUrl%22%3A+video.asset-%3Eurl%0A%7D&%24tagged=%22terminal%22&perspective=drafts

Top categories

Loading Svelte Themes