Skip to content

Instantly share code, notes, and snippets.

@rylanharper
Created August 22, 2025 00:35
Show Gist options
  • Select an option

  • Save rylanharper/8fc24ea6fc87253d39c730fd2d21dd2e to your computer and use it in GitHub Desktop.

Select an option

Save rylanharper/8fc24ea6fc87253d39c730fd2d21dd2e to your computer and use it in GitHub Desktop.
A color-selector Sanity UI component
import { Flex, Stack, Text } from '@sanity/ui'
import React, { useCallback } from 'react'
import { set, StringInputProps, unset } from 'sanity'
type ColorCircleProps = {
colorName: string
hex: string
active: boolean
onClickHandler: (val: string) => void
}
const ColorCircle = ({ colorName, hex, active, onClickHandler }: ColorCircleProps) => {
return (
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
minWidth: '60px',
gap: '8px',
marginTop: '2px',
}}>
<div
style={{
width: '28px',
height: '28px',
borderRadius: '50%',
backgroundColor: hex,
border: '2px solid var(--card-border-color)',
cursor: 'pointer',
transition: 'box-shadow 0.15s ease',
boxShadow: active
? '0 0 0 1.5px var(--card-focus-ring-color)'
: '0 0 0 1.5px transparent',
}}
onClick={() => onClickHandler(hex)}
/>
<Text size={1} align="center" muted>
{colorName}
</Text>
</div>
)
}
type ColorObject = {
title: string
value: string
preview: string
}
// Default Tailwind colors
const DEFAULT_COLORS: ColorObject[] = [
{ title: 'Black', value: 'black', preview: '#000000' },
{ title: 'White', value: 'white', preview: '#ffffff' },
{ title: 'Stone', value: 'stone', preview: '#a8a29e' },
{ title: 'Neutral', value: 'neutral', preview: '#a3a3a3' },
{ title: 'Zinc', value: 'zinc', preview: '#a1a1aa' },
{ title: 'Gray', value: 'gray', preview: '#9ca3af' },
{ title: 'Slate', value: 'slate', preview: '#94a3b8' },
{ title: 'Red', value: 'red', preview: '#f87171' },
{ title: 'Rose', value: 'rose', preview: '#fb7185' },
{ title: 'Pink', value: 'pink', preview: '#f472b6' },
{ title: 'Fuchsia', value: 'fuchsia', preview: '#e879f9' },
{ title: 'Purple', value: 'purple', preview: '#c084fc' },
{ title: 'Violet', value: 'violet', preview: '#a78bfa' },
{ title: 'Indigo', value: 'indigo', preview: '#818cf8' },
{ title: 'Blue', value: 'blue', preview: '#60a5fa' },
{ title: 'Sky', value: 'sky', preview: '#38bdf8' },
{ title: 'Cyan', value: 'cyan', preview: '#22d3ee' },
{ title: 'Teal', value: 'teal', preview: '#2dd4bf' },
{ title: 'Emerald', value: 'emerald', preview: '#34d399' },
{ title: 'Green', value: 'green', preview: '#4ade80' },
{ title: 'Lime', value: 'lime', preview: '#a3e635' },
{ title: 'Yellow', value: 'yellow', preview: '#facc15' },
{ title: 'Amber', value: 'amber', preview: '#fbbf24' },
{ title: 'Orange', value: 'orange', preview: '#fb923c' },
]
type ColorSelectorProps = StringInputProps & {
list?: ColorObject[]
}
export const ColorSelector = ({ value = '', onChange, list }: ColorSelectorProps) => {
const handleSelect = useCallback(
(val: string) => onChange(val && val !== value ? set(val) : unset()),
[onChange, value]
)
const colorList = list || DEFAULT_COLORS
return (
<Stack space={3}>
<Flex direction="row" wrap="wrap" gap={4}>
{colorList.map((colorItem) => (
<ColorCircle
key={colorItem.value}
colorName={colorItem.title}
hex={colorItem.preview}
active={colorItem.value === value}
onClickHandler={() => handleSelect(colorItem.value)}
/>
))}
</Flex>
</Stack>
)
}
@rylanharper

Copy link
Copy Markdown
Author

Then it would be used like this:

fields: [
  defineField({
    name: 'color',
    title: 'Color',
    type: 'string',
    description: 'Controls the text and button color for this block. Pick from the predefined colors to ensure consistent styling across the site.',
    components: { input: ColorSelector },
  }),
],

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment