Sanity Media Plugin

repository·main·Indexed 41 days ago

https://github.com/sanity-io/sanity-plugin-media

A media management plugin for Sanity Studio V3 (version 4.3.1) that provides a media library, asset browsing, and tagging capabilities. It includes specialized components for virtualized asset grids and tables, an auto-tagging input for schema fields, and tools for editing asset metadata and tracking document references.

Tokens
14.6K
Snippets
48
Records
60
Agent score
51%

What's inside Sanity Media Plugin

  1. Manage assets state with AssetsReducerState

    main

    The AssetsReducerState defines the shape of the asset management state within the plugin. It tracks all asset IDs, the actual asset items by their ID, pagination details, current view (grid/list), and sorting orders.

    Key properties include:

    • allIds: An array of all loaded asset IDs.
    • byIds: A record mapping asset IDs to AssetItem objects.
    • assetTypes: The types of assets currently being managed.
    • order: The current sorting configuration (direction, field, and title).
    • view: The current browser view (e.g., 'grid').
    • fetching: A boolean indicating if an asset fetch is in progress.
    • lastPicked: The ID of the last asset selected/picked.
  2. Understand the Tag lifecycle and side effects

    main

    The tags module uses Redux-Observable Epics to handle complex asynchronous logic and Sanity transactions:

    1. Creation: tagsCreateEpic checks if a tag name already exists using checkTagName before creating the document in Sanity.
    2. Deletion: tagsDeleteEpic performs a multi-step transaction. It first fetches all assets (files or images) that reference the tag, then creates a transaction to unset the tag references from those assets before finally deleting the tag document itself.
    3. Fetching: tagsFetchEpic executes a GROQ query provided in the fetchRequest action to populate the state.
    4. Updates: tagsUpdateEpic validates the new name and patches the existing tag document.
    5. Real-time Sync: The module includes 'listener' epics (e.g., tagsListenerCreateQueueEpic) that buffer incoming real-time events from Sanity to batch-update the local state, ensuring the UI stays in sync with the database efficiently.
  3. Understand Asset types: FileAsset and ImageAsset

    main

    The plugin works with two primary asset types, both of which extend standard Sanity asset documents with CustomFields:

    1. FileAsset: Represents a sanity.fileAsset.
    2. ImageAsset: Represents a sanity.imageAsset. It includes an additional creditLine field which can be a LocalizedString (either a plain string or a Record<string, string> for localized values).

    Both types support the following custom fields:

    • altText: Localized string for accessibility.
    • description: Localized string for asset description.
    • title: Localized string for the asset title.
    • opt: An optional object containing media.tags (an array of SanityReference).
    type CustomFields = {
      altText?: LocalizedString
      description?: LocalizedString
      opt?: {
        media?: {
          tags?: SanityReference[]
        }
      }
      title?: LocalizedString
    }
    
    export type FileAsset = SanityAssetDocument &
      CustomFields & {
        _type: 'sanity.fileAsset'
      }
    
    export type ImageAsset = SanityImageAssetDocument &
      CustomFields & {
        _type: 'sanity.imageAsset'
        creditLine?: LocalizedString
      }
  4. Install and add the media plugin

    main

    To enable the media library features in your Sanity Studio, add the media() plugin to your defineConfig plugins array.

    import {defineConfig} from 'sanity'
    import {media} from './src'
    
    export default defineConfig({
      // ... other config
      plugins: [
        media(),
        // ... other plugins
      ]
    })
  5. Configure MediaToolOptions

    main

    When initializing the media plugin, you can provide a MediaToolOptions object to customize its behavior. Key options include:

    • maximumUploadSize: Limit the size of uploads.
    • createTagsOnUpload: Whether to allow creating new tags during the upload process.
    • directUploads: Enable direct uploads to the asset provider.
    • locales: An array of Locale objects following the Sanity localization scheme (e.g., [{ id: 'en', title: 'English' }]).
    • creditLine: Configuration for credit lines, including an enabled boolean and excludeSources to filter specific sources.
    • components: Allows overriding the details component. The custom component receives DetailsProps and a renderDefaultDetails function to maintain standard functionality.
    export type MediaToolOptions = {
      maximumUploadSize?: number
      createTagsOnUpload?: boolean
      components?: {
        details?: ComponentType<
          DetailsProps & {renderDefaultDetails: (props: DetailsProps) => JSX.Element}
        >
      }
      creditLine?: {
        enabled: boolean
        excludeSources?: string | string[]
      }
      directUploads?: boolean
      locales?: Locale[]
    }
  6. Configure the media field with mediaTags

    main

    To add a media picker to a schema field, use the mediaField function. You can use the mediaTags option to filter the media library, ensuring that only assets tagged with specific strings (e.g., 'product') are available for selection in that field. This is useful for organizing assets by content type.

    import {mediaField} from './src'
    
    // Inside your schema definition:
    {
      name: 'image',
      title: 'Image',
      type: 'image',
      mediaTags: ['product']
    }
  7. Define media fields with automatic tagging using mediaField()

    main

    The mediaField utility simplifies the creation of image or file fields in your Sanity schema. When you provide mediaTags at the top level of the configuration, the utility performs two actions:

    1. Pre-filtering: It moves the mediaTags into options.mediaTags, which allows the media browser to pre-filter assets based on those tags.
    2. Automatic Component Wiring: It automatically wires up the AutoTagInput component as the field's input component, enabling automatic media tag application when an asset is selected.

    Usage for Images

    To create an image field, set type: 'image' and provide an array of strings to mediaTags.

    Usage for Files

    To create a file field, set type: 'file' and provide an array of strings to mediaTags.

    import {mediaField} from 'sanity-plugin-media'
    
    // For image fields
    mediaField({
      name: 'coverImage',
      type: 'image',
      mediaTags: ['product-cover'],
      options: { hotspot: true },
    })
    
    // For file fields
    mediaField({
      name: 'drawing',
      type: 'file',
      mediaTags: ['model-drawing']
    })
  8. FormSubmitButton Props

    main

    The FormSubmitButton component accepts the following props:

    PropTypeDescription
    disabledbooleanControls whether the button is clickable.
    isValidbooleanDetermines the content of the tooltip (valid vs error state).
    lastUpdatedstring (optional)An ISO date string used to show the last update time in the tooltip.
    onClick() => voidThe callback function executed when the button is clicked.
  9. Manage dialogs via dialogActions

    main

    The dialogActions object provides Redux actions to control the visibility and state of various dialogs within the media plugin. You can use these actions to show specific dialog types (like asset editing, tag creation, or confirmation prompts) or to clear/remove existing dialogs.

    Available Actions

    Showing Dialogs

    • showAssetEdit({ assetId: string }): Opens the asset editing dialog for a specific asset.
    • showTags(): Opens the tags dialog.
    • showSearchFacets(): Opens the search facets dialog.
    • showTagCreate(): Opens the dialog to create a new tag.
    • showTagEdit({ tagId: string }): Opens the dialog to edit an existing tag.

    Confirmation Dialogs

    These actions trigger confirmation prompts for destructive or significant operations. They automatically configure the confirmText, title, and confirmCallbackAction (the action to run if the user confirms).

    • showConfirmAssetsTagAdd({ assetsPicked: AssetItem[], tag: Tag, closeDialogId?: string }): Confirms adding a tag to multiple assets.
    • showConfirmAssetsTagRemove({ assetsPicked: AssetItem[], tag: Tag, closeDialogId?: string }): Confirms removing a tag from multiple assets.
    • showConfirmDeleteAssets({ assets: AssetItem[], closeDialogId?: string }): Confirms the permanent deletion of multiple assets.
    • showConfirmDeleteTag({ tag: Tag, closeDialogId?: string }): Confirms the permanent deletion of a tag.

    State Management

    • clear(): Removes all active dialogs from the state.
    • remove({ id: string }): Removes a specific dialog by its unique ID.
    • inlineTagCreate({ assetId: string, tag: Tag }): Updates an active assetEdit dialog with a newly created inline tag.
    • inlineTagRemove({ tagIds: string[] }): Updates an active assetEdit dialog to reflect removed tags.
    import { dialogActions } from './modules/dialog';
    
    // Example: Show asset edit dialog
    dispatch(dialogActions.showAssetEdit({ assetId: 'asset-123' }));
    
    // Example: Clear all dialogs
    dispatch(dialogActions.clear());
  10. Use tagsActions to dispatch tag operations

    main

    The tagsActions object contains all the action creators generated by the tagsSlice. These can be used to trigger side effects (via Epics) or update the local Redux state. Common actions include:

    • createRequest({ assetId?: string; name: string }): Initiates the creation of a new tag.
    • deleteRequest({ tag: Tag }): Initiates the deletion of a tag.
    • fetchRequest({ query: string }): Initiates a fetch for tags using a GROQ query.
    • updateRequest({ closeDialogId?: string; formData: Record<string, any>; tag: Tag }): Initiates a tag update.
    • panelVisibleSet({ panelVisible: boolean }): Toggles the visibility of the tag panel.
    import { tagsActions } from './modules/tags';
    
    // Example: Requesting a tag creation
    dispatch(tagsActions.createRequest({ name: 'New Tag' }));
  11. Manage search state with searchActions

    main

    The searchActions object provides Redux actions for managing the search state, including facets (filters) and the search query. You can use these actions to add, clear, remove, or update search facets and the text query.

    Available Actions

    ActionPayload TypeDescription
    facetsAdd{ facet: SearchFacetInputProps }Adds a new search facet with a generated unique ID.
    facetsClearvoidRemoves all existing search facets.
    facetsRemoveByName{ facetName: string }Removes a facet matching the provided name.
    facetsRemoveByTag{ tagId: string }Removes a specific tag facet if it matches the provided tagId.
    facetsRemoveById{ facetId: string }Removes a facet matching the provided unique ID.
    facetsUpdate{ modifier?: string, name: string, operatorType?: SearchFacetOperatorType, value?: any }Updates an existing facet identified by its name.
    facetsUpdateById{ modifier?: string, id: string, operatorType?: SearchFacetOperatorType, value?: any }Updates an existing facet identified by its unique id.
    querySet{ searchQuery: string }Sets the current search text query.
    import { searchActions } from './modules/search';
    
    // Examples of usage:
    searchActions.facetsAdd({ facet: myFacetConfig });
    searchActions.facetsClear();
    searchActions.querySet({ searchQuery: 'my search term' });
    searchActions.facetsRemoveByName({ facetName: 'category' });
  12. Get the asset form schema with getAssetFormSchema()

    main

    The getAssetFormSchema function returns a Zod schema used for validating asset metadata forms.

    It validates an object with the following structure:

    • altText: Localized string schema.
    • creditLine: Localized string schema.
    • description: Localized string schema.
    • opt: An object containing media, which contains an array of tags (using tagOptionSchema) or null.
    • originalFilename: A non-empty trimmed string.
    • title: Localized string schema.

    You can pass a locales array (e.g., [{id: 'en'}]) to customize the localization of the text fields.

    import { getAssetFormSchema } from './src/formSchema/index';
    
    const schema = getAssetFormSchema([{ id: 'en' }, { id: 'es' }]);
    
    // Example valid data
    const validData = {
      altText: { en: 'A photo', es: 'Una foto' },
      creditLine: { en: 'John Doe' },
      description: { en: 'Description' },
      opt: {
        media: {
          tags: [{ label: 'Nature', value: 'nature' }]
        }
      },
      originalFilename: 'photo.jpg',
      title: { en: 'Nature Photo' }
    };
    
    schema.parse(validData);