# Sanity

The long story of each car type lives in Sanity, and Mellow keeps its facts: specs, pictures, features and the price. An editor picks the car type in a string field whose input reads Mellow's catalog feed. The page then shows the editor's text beside the widget for that car type.

:::caution{title="Not tried yet"}
Mellow has not tried this recipe in a Sanity Studio yet. The input follows Sanity's documented API for a custom input component.
:::

## The catalog feed

In Mellow, open **Settings · Widget** and the widget. Under **Show more**, press **Copy CMS link**. It is the address of the widget's catalog feed, such as:

```
https://app.mellow.is/feeds/catalog/your-rental/your-channel
```

It needs no key, and any site may read it from the browser. It answers:

```json
{
  "rental": "your-rental",
  "channel": "your-channel",
  "carTypes": [
    {
      "id": "…",
      "code": "CAMPER4",
      "slug": "camper-4x4",
      "name": "Camper 4x4",
      "kind": "camper",
      "image": "https://…",
      "updatedAt": "…"
    }
  ]
}
```

## 1. The input

A string field keeps the car type's code. Its input is a list of the car types in the feed:

```tsx
// schemas/car-type-input.tsx
import { useEffect, useState } from "react";
import { set, unset, type StringInputProps } from "sanity";
import { Select } from "@sanity/ui";

const FEED = "https://app.mellow.is/feeds/catalog/your-rental/your-channel";

type CarType = { id: string; code: string; name: string };

export function CarTypeInput(props: StringInputProps) {
  const { value, onChange, elementProps } = props;
  const [list, setList] = useState<CarType[]>([]);

  useEffect(() => {
    fetch(FEED)
      .then((res) => res.json())
      .then((feed) => setList(feed.carTypes));
  }, []);

  return (
    <Select
      {...elementProps}
      value={value ?? ""}
      onChange={(event) => {
        const code = event.currentTarget.value;
        onChange(code ? set(code) : unset());
      }}
    >
      <option value="">Pick a car type</option>
      {list.map((c) => (
        <option key={c.id} value={c.code}>
          {c.name} ({c.code})
        </option>
      ))}
    </Select>
  );
}
```

## 2. The field

```ts
// schemas/car-page.ts
import { defineField, defineType } from "sanity";
import { CarTypeInput } from "./car-type-input";

export const carPage = defineType({
  name: "carPage",
  title: "Car type page",
  type: "document",
  fields: [
    defineField({ name: "title", type: "string" }),
    defineField({
      name: "carType",
      title: "Car type in Mellow",
      type: "string",
      components: { input: CarTypeInput },
    }),
    defineField({ name: "story", type: "array", of: [{ type: "block" }] }),
  ],
});
```

## 3. The page

Read `carType` with the page, such as with GROQ `*[_type == "carPage" && slug.current == $slug][0]{ title, story, carType }`, and give it to `<mellow-car-type id="…">` and to the search's `car-type`, as in [One car type on its own page](/widget/start#one-car-type-on-its-own-page). The [Prismic recipe](/widget/recipes/prismic#3-the-page) shows that markup in a React component, and the [Nuxt recipe](/widget/recipes/nuxt) the same in Vue.

The code is the join between the two: staff may change a car type's code in Mellow. Then the editor picks the car type again. To join on the `id` instead, which never changes, put `c.id` in the option's `value`. `<mellow-car-type>` takes either.
