npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

storyloco

v1.14.5

Published

A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS

Readme

Storyloco

A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS. Each plugin is designed to be lightweight, performant, and a joy to use.

🚀 What's Included

Field Plugins

  • 🎬 Mux Video - Upload, manage, and configure videos with Mux integration
  • 📎 Asset Plus - Enhanced asset picker with search, filters, and previews
  • 📝 Heading - Simple heading editor with level selection (H1-H6)
  • 🎨 Theme - Color theme selector with visual previews
  • 🔗 Link - Link editor with support for internal, external, email, and asset links
  • 🔍 SEO - SEO metadata editor with support for title, description, OG tags, and Twitter cards
  • 🏗️ Plans - Floor plan manager with drag-and-drop sorting for types, floors, dimensions, and rooms
  • 📝 Input - Comprehensive form input field with support for all HTML input types, checkboxes, radio buttons, selects, and textareas
  • ⏰ Time - Simple time input field with step control

Vite Plugins

  • 📋 Storyblok Schema - Automatically generate TypeScript types from your Storyblok components
  • 🔄 Storyblok Redirects - Generate and handle redirects from Storyblok datasource entries

Storyblok Client

  • 🎯 StoryblokClient - Reactive wrapper for Storyblok with Svelte 5 integration
  • 🔧 Utilities - Re-exported editable, Block, and richtext functions

Shared Components

A comprehensive UI component library built with:

  • Svelte 5 - Latest reactive framework
  • Tailwind CSS - Utility-first styling
  • Bits UI - Headless component primitives
  • TypeScript - Full type safety

📦 Installation

bun add storyloco

🎯 Usage

Import Types

// Import specific plugin types
import type { Video } from 'storyloco/mux'
import type { Heading } from 'storyloco/heading'
import type { Link } from 'storyloco/link'
import type { Input } from 'storyloco/input'
import type { Asset } from 'storyloco/asset'
import type { SEO } from 'storyloco/seo'

// Or import everything
import type { Video, Heading, Link, Input, Asset, SEO } from 'storyloco'

Use Storyblok Client

1. Create your Storyblok client

$lib/storyblok.ts:

import { StoryblokClient } from 'storyloco'
import { PUBLIC_STORYBLOK_ACCESS_TOKEN } from '$env/static/public'

// Create client (defaults to $lib/blocks/*.svelte) - init() called automatically
export const client = new StoryblokClient(PUBLIC_STORYBLOK_ACCESS_TOKEN)

// With custom component globs
export const client = new StoryblokClient(
	PUBLIC_STORYBLOK_ACCESS_TOKEN,
	import.meta.glob(['$blocks/*.svelte', '$templates/*.svelte'])
)

// Single glob pattern
export const client = new StoryblokClient(PUBLIC_STORYBLOK_ACCESS_TOKEN, import.meta.glob('$blocks/*.svelte'))

2. Use in your Svelte components

+page.svelte:

<script lang="ts">
	import { Block } from 'storyloco'
	import { client } from '$lib/storyblok.js'

	let { data: _data } = $props()

	// Connect story for live preview updates
	const data = $derived(client.connect(_data.story))
</script>

<!-- Render blocks -->
{#each data.story?.content.blocks || [] as blok}
	<Block {blok} />
{/each}

3. Create your component structure

$lib/blocks/hero.svelte:

<script lang="ts">
	import { editable } from 'storyloco'

   // This is auto-generated by the Storyblok Schema plugin
   import type { Blok, Hero } from '$lib/components.schema.js'

   interface Props {
      blok: Blok<Hero>
   }

	let { blok } = $props()
</script>

<section use:editable={blok}>
	<h1>{blok.headline}</h1>
	<p>{blok.subheadline}</p>
</section>

Use Shared Components

import { Input, Label, Switch, Skeleton, Select, Separator } from 'storyloco/shared'
import { cn } from 'storyloco/shared/utils'

Vite Plugins

Storyblok Schema Plugin

Automatically generate TypeScript types from your Storyblok components during development:

// vite.config.ts
import { schema } from 'storyloco/vite'

export default defineConfig({
	plugins: [
		schema({
			output_path: 'src/lib',
			interval_ms: 60_000 // regenerate every minute
		})
	]
})

Options:

  • storyblok_personal_access_token - Your Storyblok personal access token (defaults to STORYBLOK_PERSONAL_ACCESS_TOKEN env var)
  • storyblok_space_id - Your Storyblok space ID (defaults to STORYBLOK_SPACE_ID env var)
  • output_path - Where to output generated files (defaults to src/lib)
  • filename - Name of the generated file (defaults to components.schema.ts)
  • interval_ms - How often to regenerate types (defaults to 60000ms)

The plugin will create a file called components.schema.ts (auto-generated) in the src/lib directory. This file will be updated automatically when you make changes to your Storyblok components as per the interval specified.

Custom Plugin Types:

You can define custom types for Storyblok field plugins by exporting an interface in your vite config:

// vite.config.ts
import { schema } from 'storyloco/vite'

export interface StoryblokCustomPlugins {
	'theme-field': string
	'heading-field': import('storyloco/heading').Heading
	'loco-link-field': import('storyloco/link').Link
	'mux-field': import('storyloco/mux').Video
	'loco-input': import('storyloco/input').Input
	'seo-metatags': {
		plugin: 'seo_metatags'
		_uid: import('crypto').UUID
		title?: string
		og_image?: string
		og_title?: string
		description?: string
		twitter_image?: string
		twitter_title: string
		og_description: string
		twitter_description: string
	}
	'loco-time': string
	'loco-asset': import('storyloco/asset').Asset
}

export default defineConfig({
	plugins: [
		schema({
			output_path: 'src/lib',
			interval_ms: 60000
		})
	]
})

The plugin will:

  • Pull your component schema from Storyblok
  • Generate TypeScript definitions with proper types
  • Use your custom plugin types when available
  • Format the output with Prettier
  • Lock the generated file to prevent manual edits
  • Regenerate automatically when components change

Storyblok Redirects Plugin

Generate and handle redirects from Storyblok datasource entries:

// vite.config.ts
import { redirects } from 'storyloco/vite'

export default defineConfig({
	plugins: [
		redirects({
			datasource: 'redirects',
			public_storyblok_access_token: 'your-token'
		})
	]
})

Options:

  • datasource - The Storyblok datasource name (defaults to 'redirects')
  • public_storyblok_access_token - Your public access token (defaults to PUBLIC_STORYBLOK_ACCESS_TOKEN env var)

SvelteKit Integration:

// src/hooks.server.ts
import { handle_redirects } from 'storyloco/vite'

export const handle = handle_redirects

The plugin will:

  • Fetch redirect entries from your Storyblok datasource
  • Generate a redirects map during build
  • Support exact matches and wildcard patterns
  • Handle both internal and external redirects

🔧 Development

Prerequisites

  • Bun (recommended) or Node.js 18+
  • Storyblok account for field plugin development

Setup

# Clone the repo
git clone <your-repo-url>
cd storyloco

# Install dependencies
bun install

# Start development
bun run dev

Project Structure

storyloco/
├── packages/
│   ├── mux/          # Video upload & management plugin
│   ├── heading/      # Heading editor plugin
│   ├── theme/        # Theme selector plugin
│   ├── link/         # Link editor plugin
│   ├── seo/          # SEO metadata editor plugin
│   ├── input/        # Form input field plugin
│   └── shared/       # UI component library
├── src/
│   └── index.ts      # Type exports
└── package.json      # Root workspace config

Adding New Plugins

  1. Create plugin package:

    bun run add-plugin
  2. Export types in your plugin:

    Create a types.ts file in your plugin package:

    // packages/your-plugin/types.ts
    export interface YourType {
    	// your interface here
    }
  3. Add to root exports in package.json:

    {
    	"exports": {
    		"./your-plugin": {
    			"types": "./packages/your-plugin/types.ts",
    			"import": "./packages/your-plugin/src/app.svelte"
    		}
    	}
    }
  4. Re-export in src/index.ts:

    export type { YourType } from '../packages/your-plugin/types'

🎨 Shared Components

The shared package provides a comprehensive set of UI components:

Form Components

  • Input - Text input with proper styling
  • Label - Accessible form labels
  • Switch - Toggle switch component
  • Select - Select component
  • Separator - Separator component

Display Components

  • Skeleton - Loading skeleton component

Utilities

  • cn() - Class name utility (clsx + tailwind-merge)
  • setup() - Global setup function

🔌 Field Plugin Development

Each plugin follows the Storyblok field plugin pattern:

<script lang="ts">
  import { createFieldPlugin, type FieldPluginResponse } from '@storyblok/field-plugin'

  interface YourData {
    // your data structure
  }

  type Plugin = FieldPluginResponse<YourData | null>

  let plugin: Plugin | null = $state(null)
  let content: YourData = $state(initial_value)

  onMount(() => {
    createFieldPlugin({
      enablePortalModal: true,
      validateContent: (c) => {
        // validation logic
        return { content: validated_content }
      },
      onUpdateState: (state) => {
        plugin = state as Plugin
      },
    })
  })
</script>

🚀 Deployment

Deploy Individual Plugins

# From the plugin directory
cd packages/mux
bun run deploy

Build Shared Package

cd packages/shared
bun run build

🛠️ Tech Stack

  • Framework: Svelte 5 with TypeScript
  • Styling: Tailwind CSS
  • Components: Shadcn Svelte, Bits UI primitives
  • Build Tool: Vite
  • Package Manager: Bun (recommended)
  • Linting: ESLint + Prettier

📄 License

MIT License - feel free to use this however you want, you absolute legend! 🎯

🤝 Contributing

  1. Fork the repo
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Built with ❤️ and a lot of swearing by the Storyloco team