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

sanity-plugin-link

v1.0.4

Published

A small Sanity Studio link field for choosing a label, a URL, or a weak reference.

Downloads

684

Readme

sanity-plugin-link

A small Sanity Studio link field for choosing a label, a URL, and optionally a weak reference, inspired by Shopify's link picker.

It registers a single link object type with two built-in fields:

  • label - editor-facing link text, like Shop now
  • url - a path, query string, hash, URL, email address, or phone number

When document routes are configured, it also adds:

  • reference - a weak Sanity reference for internal links

The plugin stays intentionally unopinionated. It stores the link data, then lets your GROQ projection and frontend decide how references turn into URLs.

On the Studio side, the input prevents invalid URL values. When url is present, it is already normalized as a valid internal path, query string, hash, external URL, mailto:, or tel: link.

Installation

npm install sanity-plugin-link

Usage

Add the plugin to sanity.config.ts:

import {defineConfig} from 'sanity'
import {linkPlugin} from 'sanity-plugin-link'

export default defineConfig({
  // ...
  plugins: [linkPlugin()],
})

Use the link type anywhere in your schema:

import {defineField, defineType} from 'sanity'

export const hero = defineType({
  name: 'hero',
  title: 'Hero',
  type: 'object',
  fields: [
    defineField({
      name: 'link',
      title: 'Link',
      type: 'link',
    }),
  ],
})

Stored Value

A saved link value looks like this:

type LinkValue = {
  _type?: 'link'
  label?: string | null
  url?: string | null
  reference?: {
    _type: 'reference'
    _ref: string
    _weak?: boolean
  } | null
}

For external links, paths, emails, phone numbers, hashes, and query strings, the value is stored in url.

For internal document links, configure document routes first. Those links are stored in reference.

Querying Links

Project the fields your frontend needs. The plugin does not assume what an internal URL should look like.

*[_type == "page" && slug.current == $slug][0] {
  title,
  link {
    label,
    url,
    reference->{
      _type,
      slug
    }
  }
}

Then resolve the href in your app:

type ProjectedLink = {
  label?: string | null
  url?: string | null
  reference?: {
    _type?: string
    slug?: {current?: string}
  } | null
}

function resolveLink(link?: ProjectedLink) {
  if (!link) return null
  if (link.url) return link.url

  if (link.reference?._type === 'page' && link.reference.slug?.current) {
    return `/${link.reference.slug.current}`
  }

  return null
}

Or resolve the final URL directly in GROQ if your route rules are simple:

*[_type == "page" && slug.current == $slug][0] {
  title,
  link {
    label,
    "url": coalesce(
      select(
        reference->_type == "product" => "/products/" + reference->store.slug.current,
        reference->_type == "collection" => "/collections/" + reference->slug.current
      ),
      url
    )
  }
}

That keeps the frontend shape as {label, url}. Reference URLs are inferred first, and the stored url field is used as the fallback.

Link Picker Routes

The field works without configuration for labels and URL values. Add routes when you want the picker to offer shortcuts or browseable document folders.

import {defineConfig} from 'sanity'
import {linkPlugin, linkRoute} from 'sanity-plugin-link'

export default defineConfig({
  // ...
  plugins: [
    linkPlugin({
      routes: [
        linkRoute.route('Home', '/'),
        linkRoute.route('Shop', '/shop'),
        linkRoute.documents('Pages', 'page'),
        linkRoute.documents('Products', 'product'),
      ],
    }),
  ],
})

You can also override routes on a single field. Field-level overrides can narrow or customize the picker, but document routes still need to be declared at the plugin level so Sanity can define accepted reference types.

defineField({
  name: 'link',
  title: 'Link',
  type: 'link',
  options: {
    routes: [
      linkRoute.route('Contact', '/contact'),
      linkRoute.documents('Products', 'product'),
    ],
  },
})

Document routes control which reference types the picker can select. Static routes write to url; document selections write to reference.

Extending the Field

If your project needs extra fields, append them at the plugin level. You can also configure the Sanity preview for the link object with preview:

import {defineField} from 'sanity'

linkPlugin({
  fields: [
    defineField({
      name: 'parameters',
      title: 'Parameters',
      type: 'string',
    }),
    defineField({
      name: 'anchor',
      title: 'Anchor',
      type: 'string',
    }),
  ],
  preview: {
    select: {
      title: 'label',
      subtitle: 'url',
    },
  },
})

Keep the default fields stable when possible. Most projects can model extra frontend behavior in GROQ or in their link resolver without changing the stored shape.

API

import {
  defineLinkMenu,
  linkPlugin,
  linkRoute,
  type LinkFieldOptions,
  type LinkFieldPluginOptions,
  type LinkRouteDefinition,
  type LinkValue,
} from 'sanity-plugin-link'

License

MIT © Dennis Regalado

sanity-plugin-link