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

@liiift-studio/sanity-bulk-data-operations

v2.0.1

Published

Flexible bulk data operations utility for Sanity Studio — search, add, and modify data across any document types with safety features

Readme

Sanity Bulk Data Operations

A bulk field-editing component for Sanity Studio that searches documents by type and name, then adds or rewrites a field across all matches in one pass — with a two-tier safety model that keeps non-destructive fills separate from overwrites.

npm Sanity React license

Heads up — this tool writes to your dataset. In its default mode it only fills empty fields (setIfMissing), but Danger Mode overwrites existing values (set) and cannot be undone. Read Safety model before using it on production data.


How it works

You give it a Sanity client, pick a document type and a name to match, name a target field, and choose what to write. It runs a GROQ query, shows you every matching document, and patches them one at a time. In the safe default the search itself excludes documents where the target field is already defined (!defined(field)), so existing values are never touched; Danger Mode drops that filter and lets you overwrite or transform existing values.

Regenerate the diagram with npm run capture (source: scripts/data-flow.mmd).


Features

  • 🔍 Search by type + name — match documents of a chosen _type whose title starts with your query, with an optional exclude term.
  • ✏️ Bulk field write — set the same field across every matched document.
  • 🧪 Transform modes — full replace, find & replace, prepend, append, or type/case transforms (trim, upper/lower/capitalize, to number/boolean/array/string).
  • 🛡️ Two-tier safety — non-destructive setIfMissing by default; destructive set only behind an explicit, modal-gated Danger Mode.
  • 📊 Live preview & progress — see the matched documents and a running status message as the patches commit.
  • 🔗 Deep links — each match links straight to its document in the desk.

Installation

npm install @liiift-studio/sanity-bulk-data-operations

The package is scoped — use the full @liiift-studio/… name. There is no unscoped sanity-bulk-data-operations package.


Quick start

The package's default export is the SearchAddData component. Render it inside a Sanity Studio tool, dashboard widget, or any custom view, passing it a client and a small amount of state to track Danger Mode.

import {useState} from 'react'
import {useClient} from 'sanity'
import {EditIcon} from '@sanity/icons'
import SearchAddData from '@liiift-studio/sanity-bulk-data-operations'

export default function BulkEditor() {
	const client = useClient({apiVersion: '2024-01-01'})
	const [dangerMode, setDangerMode] = useState(false)

	return (
		<SearchAddData
			client={client}
			displayName="Bulk Field Editor"
			icon={EditIcon}
			utilityId="bulk-field-editor"
			dangerMode={dangerMode}
			onDangerModeChange={(_utilityId, enabled) => setDangerMode(enabled)}
		/>
	)
}

The component manages danger-mode intent (it asks via onDangerModeChange), but you own the dangerMode boolean — keep it in state, persist it across multiple instances, or scope it however your Studio needs.

Mounting it in Studio

SearchAddData is a plain component, so wire it in wherever you put custom UI — a Studio tool, a structure-builder view, or a dashboard widget. A minimal tool registration:

// sanity.config.ts
import {defineConfig} from 'sanity'
import {EditIcon} from '@sanity/icons'
import BulkEditor from './BulkEditor' // the component from the quick start above

export default defineConfig({
	// ...project, dataset, plugins, schema...
	tools: (prev) => [
		...prev,
		{
			name: 'bulk-field-editor',
			title: 'Bulk Field Editor',
			icon: EditIcon,
			component: BulkEditor,
		},
	],
})

utilityId is a stable string you assign per instance. It is echoed back as the first argument to onDangerModeChange, so if you render several editors you can tell which one toggled Danger Mode and track each one's state independently.


Props

SearchAddData (default export):

| Prop | Type | Required | Description | |------|------|----------|-------------| | client | SanityClient | ✅ | Authenticated Sanity client (typically from useClient). Needs write access for the patches to commit. | | displayName | string | ✅ | Heading shown above the editor. | | utilityId | string | ✅ | Stable identifier for this instance, passed back in onDangerModeChange. | | dangerMode | boolean | ✅ | Whether destructive (overwrite) mode is active. You control this value. | | onDangerModeChange | (utilityId: string, enabled: boolean) => void | ✅ | Called when the user toggles Danger Mode (after confirming the warning modal). | | icon | React.ComponentType<{style?: React.CSSProperties}> | – | Optional icon rendered in the heading. |

shouldShowDangerWarning()

Also exported (named) is a helper that returns true when the Danger Mode warning modal should be shown. Users can suppress the modal for 48 hours; this helper checks the stored expiry and returns false while suppression is active.

import {shouldShowDangerWarning} from '@liiift-studio/sanity-bulk-data-operations'

if (shouldShowDangerWarning()) {
	// Show your own confirmation, or let the component's built-in modal handle it.
}

Write modes

The component always writes a single field (the "Field Name" input) across all matched documents. How it computes the new value depends on the mode:

| Mode | What it does | |------|--------------| | Full Replace | Evaluates the textarea contents as a JS value and sets the field to it. | | Find & Replace | replaceAll(find, replace) on the field's existing string value. | | Prepend / Append | Adds text to the start / end of the existing string. | | Transform | Trim, uppercase, lowercase, capitalize, or convert the field's type (to number, boolean, array, or string). |

Find & Replace, Prepend, Append, and Transform operate on the field's current value, so they require Danger Mode (they overwrite). Full Replace works in either mode — in the safe default it only fills documents where the field is missing.


Safety model

This component does bulk writes, so its safety design is deliberate:

  • Non-destructive by default. With Danger Mode off, patches use client.patch(id).setIfMissing(...), and the search query adds && !defined(field) so documents where the target field is already defined are excluded from the results entirely and never touched.
  • Destructive writes are gated. Turning Danger Mode on triggers a warning modal (DangerModeWarning) before any overwrite is possible. Only after confirming does onDangerModeChange fire with enabled: true, switching patches to client.patch(id).set(...).
  • Suppression is time-boxed. The warning can be hidden for 48 hours; after that shouldShowDangerWarning() returns true again.
  • One document at a time. Patches commit sequentially with a 50ms gap between them and a live status message, rather than firing all at once.

⚠️ eval() in Full Replace. The "Full Replace" textarea is evaluated as JavaScript so you can author rich values (arrays of objects with _keys, etc.). Only paste expressions you trust. Treat this as an admin-only tool, not something to expose to untrusted Studio users.


Requirements

Peer dependencies (declared in package.json):

  • sanity^3 || ^4 || ^5
  • @sanity/ui^1 || ^2 || ^3
  • @sanity/icons^2 || ^3
  • react^18 || ^19

Built as an ESM bundle (dist/index.js) with React, sanity, and @sanity/* left external.


License

MIT — © Liiift Studio.

Contributing

Issues and pull requests welcome: https://github.com/Liiift-Studio/sanity-bulk-data-operations/issues