đź”® Sanity Create is here. Writing is reinvented. Try now, no developer setup

Modeling a "scroll to div" link in Sanity.io using a custom string component and a dropdown menu.

5 replies
Last updated: Jan 13, 2023
How would you model a link to a section within a page? Type "scroll to div" link.
Jan 13, 2023, 12:02 PM
One solution I'm thinking of is to create a custom string component, that fetches the modules of the current document, and lists them in a dropdown. When the user selects an entry the
_key
gets stored. I can then use that in the frontend to link to the section.
Jan 13, 2023, 12:19 PM
But maybe something exists?
Jan 13, 2023, 12:19 PM
I did this. Works fine! 🙂
Jan 13, 2023, 2:09 PM
Not the most beautiful, but works!
Jan 13, 2023, 2:10 PM
I did something similar for selecting a section in a document you’re referencing in a V2 Studio (sorry I haven’t had a change to migrate it yet):
//schema
{
      name: 'referenceField',
      title: 'Field within a Reference',
      description:
        'This field uses the @sanity/client to allow you to indicate a specific field within a selected reference',
      type: 'object',
      fields: [
        {
          name: 'page',
          title: 'Page',
          type: 'reference',
          to: [{ type: 'page' }],
        },
        {
          name: 'section',
          title: 'Section',
          type: 'string',
          hidden: ({ document }) => !document.referenceField.page,
          inputComponent: ClientAsyncSelect,
        },
      ],
    },

import React, { useState, useEffect } from 'react';
import { Card, Stack, Select } from '@sanity/ui';
import { FormField } from '@sanity/base/components';
import PatchEvent, { set, unset } from '@sanity/form-builder/PatchEvent';
import { useId } from '@reach/auto-id';
import { studioClient } from '../../lib/utils/studioClient';

const ClientAsyncSelect = React.forwardRef((props, ref) => {
  const [listItems, setListItems] = useState([]);

  const {
    type, // Schema information
    value, // Current field value
    readOnly, // Boolean if field is not editable
    markers, // Markers including validation rules
    presence, // Presence information for collaborative avatars
    compareValue, // Value to check for "edited" functionality
    onFocus, // Method to handle focus state
    onBlur, // Method to handle blur state
    onChange, // Method to handle patch events,
    parent,
  } = props;

  // Creates a change handler for patching data
  const handleChange = React.useCallback(
    // useCallback will help with performance
    event => {
      const inputValue = event.currentTarget.value; // get current value
      // if the value exists, set the data, if not, unset the data
      onChange(PatchEvent.from(inputValue ? set(inputValue) : unset()));
    },
    [onChange]
  );

  const inputId = useId();

  useEffect(() => {
    const getSections = async () => {
      await studioClient
        .fetch(`*[_id == $id][0].sections[].title`, {
          id: parent.page._ref ? parent.page._ref : '',
        })
        .then(setListItems);
    };

    getSections();
  }, []);

  return (
    <FormField
      description={type.description} // Creates description from schema
      title={type.title} // Creates label from schema title
      __unstable_markers={markers} // Handles all markers including validation
      __unstable_presence={presence} // Handles presence avatars
      compareValue={compareValue} // Handles "edited" status
      inputId={inputId} // Allows the label to connect to the input field
    >
      <Card padding={0}>
        <Stack>
          <Select
            id={inputId} // A unique ID for this input
            fontSize={2}
            padding={[3, 3, 4]}
            space={[3, 3, 4]}
            value={value} // Current field value
            readOnly={readOnly} // If "readOnly" is defined make this field read only
            onFocus={onFocus} // Handles focus events
            onBlur={onBlur} // Handles blur events
            ref={ref}
            onChange={handleChange} // A function to call when the input value changes
          >
            <option value={''}>---</option>
            {listItems &&
              listItems.map(item => (
                <option key={item} value={item}>
                  {item}
                </option>
              ))}
          </Select>
        </Stack>
      </Card>
    </FormField>
  );
});

export default ClientAsyncSelect;
Jan 13, 2023, 6:30 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?