Seed Protocol
Getting started

Create and read items

Write an item to the local database from a React component, list a model's items, and show their property values as they change.

An item is one record of a model, such as a single post. Creating one writes it to the SQLite database the SDK runs in the browser. You don't need a wallet, and nothing is signed or uploaded: the item stays in the browser until you publish it, which is a separate step.

This page assumes you've followed Add Seed to your app, so your schema has a Post model with a title property and the components below render inside SeedProvider and SeedClientGate.

Create an item

useCreateItem gives you a createItem function. Pass it the model name and the property values, and the SDK saves a new item to the local database:

src/NewPost.tsx
import { useState } from 'react'
import { useCreateItem } from '@seedprotocol/react'

export function NewPost() {
  const { createItem, isLoading, error } = useCreateItem()
  const [title, setTitle] = useState('')

  const handleCreate = async () => {
    if (!title.trim()) return
    await createItem('Post', { title })
    setTitle('')
  }

  return (
    <div>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Post title"
      />
      <button onClick={handleCreate} disabled={isLoading}>
        {isLoading ? 'Saving…' : 'Add post'}
      </button>
      {error && <p>Couldn't save: {error.message}</p>}
    </div>
  )
}

createItem doesn't throw. If a value is invalid, such as a title over 255 characters, it saves nothing, returns undefined and puts the error in error. A call made while an earlier one is still running creates nothing: it returns the earlier call's promise, which resolves to that call's item whatever values the second call passed. The sample disables the button while isLoading is true, so a click during a save does nothing.

List a model's items

useItems returns the items of a model and re-renders when items are added or removed. To show a property value that stays current, each row uses useItemProperty, which subscribes to one property of one item:

src/PostList.tsx
import { useItems, useItemProperty } from '@seedprotocol/react'

function PostRow({ seedLocalId }: { seedLocalId: string }) {
  const { property } = useItemProperty({ seedLocalId, propertyName: 'title' })
  return <li>{property?.value ?? 'Untitled'}</li>
}

export function PostList() {
  const { items, isLoading } = useItems({ modelName: 'Post' })

  if (isLoading) return <p>Loading posts…</p>
  if (items.length === 0) return <p>No posts yet.</p>

  return (
    <ul>
      {items.map((item) => (
        <PostRow key={item.seedLocalId} seedLocalId={item.seedLocalId} />
      ))}
    </ul>
  )
}

seedLocalId is the item's ID in the local database. Once an item is published it also has a seedUid, its EAS attestation UID.

Render them

Put both components anywhere under SeedClientGate:

import { NewPost } from './NewPost'
import { PostList } from './PostList'

export function PostsPanel() {
  return (
    <section>
      <NewPost />
      <PostList />
    </section>
  )
}

Run the app, type a title and click Add post. The post appears in the list straight away, because useItems runs a live query on the local database. Refresh the page and it's still there.

Without React

With the client started by client.init (see Add Seed to your app), Item.create and Item.all do the same work:

import { Item } from '@seedprotocol/sdk'

const post = await Item.create({ modelName: 'Post', title: 'Hello, Seed' })

const posts = await Item.all('Post', false, { waitForReady: true })
console.log(posts.length, post.seedLocalId)

Item.all returns the items newest first, by createdAt. Unlike useItems, it reads once and doesn't update when items change. See Item.

Next steps

On this page