Grid

The Grid component is provided as an alternative to the List component when the defining characteristic of an item is an image.

Because its API tries to stick as closely to List's as possible, changing a view from List to Grid should be as simple as:

  • making sure you're using at least version 1.36.0 of the @raycast/api package

  • updating your imports from import { List } from '@raycast/api' to import { Grid } from '@raycast/api';

  • removing the isShowingDetail prop from the top-level List component, along with all List.Items' detail prop

  • renaming all List.Items' hicon prop to content

  • removing all List.Items' accessories, accessoryIcon and `accessoryTitle props; Grid.Item does not currently support accessories

  • finally, replacing all usages of List with Grid.

The search bar allows users to interact quickly with grid items. By default, Grid.Items are displayed if the user's input can be (fuzzy) matched to the item's title or keywords.

Custom filtering

Sometimes, you may not want to rely on Raycast's filtering, but use/implement your own. If that's the case, you can set the Grid's filtering prop to false, and the items displayed will be independent of the search bar's text. Note that filtering is also implicitly set to false if an onSearchTextChange listener is specified. If you want to specify a change listener and still take advantage of Raycast's built-in filtering, you can explicitly set filtering to true.

import { useEffect, useState } from "react";
import { Grid } from "@raycast/api";

const items = [
  { content: "🙈", keywords: ["see-no-evil", "monkey"] },
  { content: "🥳", keywords: ["partying", "face"] },
];

export default function Command() {
  const [searchText, setSearchText] = useState("");
  const [filteredList, filterList] = useState(items);

  useEffect(() => {
    filterList(items.filter((item) => item.keywords.some((keyword) => keyword.includes(searchText))));
  }, [searchText]);

  return (
    <Grid
      columns={5}
      inset={Grid.Inset.Large}
      filtering={false}
      onSearchTextChange={setSearchText}
      navigationTitle="Search Emoji"
      searchBarPlaceholder="Search your favorite emoji"
    >
      {filteredList.map((item) => (
        <Grid.Item key={item.content} content={item.content} />
      ))}
    </Grid>
  );
}

Other times, you may want the content of the search bar to be updated by the extension, for example, you may store a list of the user's previous searches and, on the next visit, allow them to "continue" where they left off.

To do so, you can use the searchText prop.

import { useState } from "react";
import { Action, ActionPanel, Grid } from "@raycast/api";

const items = [
  { content: "🙈", keywords: ["see-no-evil", "monkey"] },
  { content: "🥳", keywords: ["partying", "face"] },
];

export default function Command() {
  const [searchText, setSearchText] = useState("");

  return (
    <Grid
      searchText={searchText}
      onSearchTextChange={setSearchText}
      navigationTitle="Search Emoji"
      searchBarPlaceholder="Search your favorite emoji"
    >
      {items.map((item) => (
        <Grid.Item
          key={item.content}
          content={item.content}
          actions={
            <ActionPanel>
              <Action title="Select" onAction={() => setSearchText(item.content)} />
            </ActionPanel>
          }
        />
      ))}
    </Grid>
  );
}

Some extensions may benefit from giving users a second filtering dimension. A media file management extension may allow users to view only videos or only images, an image-searching extension may allow switching ssearch engines, etc.

This is where the searchBarAccessory prop is useful. Pass it a Grid.Dropdown component, and it will be displayed on the right-side of the search bar. Invoke it either by using the global shortcut P or by clicking on it.

Pagination

Pagination requires version 1.69.0 or higher of the @raycast/api package.

Grids have built-in support for pagination. To opt in to pagination, you need to pass it a pagination prop, which is an object providing 3 pieces of information:

  • onLoadMore - will be called by Raycast when the user reaches the end of the grid, either using the keyboard or the mouse. When it gets called, the extension is expected to perform an async operation which eventually can result in items being appended to the end of the grid.

  • hasMore - indicates to Raycast whether it should call onLoadMore when the user reaches the end of the grid.

  • pageSize - indicates how many placeholder items Raycast should add to the end of the grid when it calls onLoadMore. Once onLoadMore finishes executing, the placeholder items will be replaced by the newly-added grid items.

Note that extensions have access to a limited amount of memory. As your extension paginates, its memory usage will increase. Paginating extensively could lead to the extension eventually running out of memory and crashing. To protect against the extension crashing due to memory exhaustion, Raycast monitors the extension's memory usage and employs heuristics to determine whether it's safe to paginate further. If it's deemed unsafe to continue paginating, onLoadMore will not be triggered when the user scrolls to the bottom, regardless of the hasMore value. Additionally, during development, a warning will be printed in the terminal.

For convenience, most of the hooks that we provide have built-in pagination support. Here's an example of how to add pagination support to a simple command using usePromise, and one "from scratch".

import { setTimeout } from "node:timers/promises";
import { useState } from "react";
import { Grid } from "@raycast/api";
import { usePromise } from "@raycast/utils";

export default function Command() {
  const [searchText, setSearchText] = useState("");

  const { isLoading, data, pagination } = usePromise(
    (searchText: string) => async (options: { page: number }) => {
      await setTimeout(200);
      const newData = Array.from({ length: 25 }, (_v, index) => ({ index, page: options.page, text: searchText }));
      return { data: newData, hasMore: options.page < 10 };
    },
    [searchText]
  );

  return (
    <Grid isLoading={isLoading} onSearchTextChange={setSearchText} pagination={pagination}>
      {data?.map((item) => (
        <Grid.Item
          key={`${item.index} ${item.page} ${item.text}`}
          content=""
          title={`Page: ${item.page} Item ${item.index}`}
          subtitle={item.text}
        />
      ))}
    </Grid>
  );
}

Examples

import { Grid } from "@raycast/api";

export default function Command() {
  return (
    <Grid columns={8} inset={Grid.Inset.Large}>
      <Grid.Item content="🥳" />
      <Grid.Item content="🙈" />
    </Grid>
  );
}

API Reference

Grid

Displays Grid.Sections or Grid.Items.

The grid uses built-in filtering by indexing the title & keywords of its items.

Example

import { Grid } from "@raycast/api";

const items = [
  { content: "🙈", keywords: ["see-no-evil", "monkey"] },
  { content: "🥳", keywords: ["partying", "face"] },
];

export default function Command() {
  return (
    <Grid
      columns={5}
      inset={Grid.Inset.Large}
      navigationTitle="Search Emoji"
      searchBarPlaceholder="Search your favorite emoji"
    >
      {items.map((item) => (
        <Grid.Item key={item.content} content={item.content} keywords={item.keywords} />
      ))}
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

actions

A reference to an ActionPanel. It will only be shown when there aren't any children.

React.ReactNode

-

aspectRatio

Aspect ratio for the Grid.Item elements. Defaults to 1.

"1" or "3/2" or "2/3" or "4/3" or "3/4" or "16/9" or "9/16"

-

children

Grid sections or items. If Grid.Item elements are specified, a default section is automatically created.

React.ReactNode

-

columns

Column count for the grid's sections. Minimum value is 1, maximum value is 8.

number

5

filtering

Toggles Raycast filtering. When true, Raycast will use the query in the search bar to filter the items. When false, the extension needs to take care of the filtering.

boolean or { keepSectionOrder: boolean }

false when onSearchTextChange is specified, true otherwise.

fit

Fit for the Grid.Item element content. Defaults to "contain"

-

inset

Indicates how much space there should be between a Grid.Items' content and its borders. The absolute value depends on the value of the itemSize prop.

-

isLoading

Indicates whether a loading bar should be shown or hidden below the search bar

boolean

false

navigationTitle

The main title for that view displayed in Raycast

string

Command title

pagination

Configuration for pagination

{ hasMore: boolean; pageSize: number; onLoadMore: (page: number) => void }

-

searchBarAccessory

Grid.Dropdown that will be shown in the right-hand-side of the search bar.

ReactElement<List.Dropdown.Props, string>

-

searchBarPlaceholder

Placeholder text that will be shown in the search bar.

string

"Search…"

searchText

The text that will be displayed in the search bar.

string

-

selectedItemId

Selects the item with the specified id.

string

-

throttle

Defines whether the onSearchTextChange handler will be triggered on every keyboard press or with a delay for throttling the events. Recommended to set to true when using custom filtering logic with asynchronous operations (e.g. network requests).

boolean

false

onSearchTextChange

Callback triggered when the search bar text changes.

(text: string) => void

-

onSelectionChange

Callback triggered when the item selection in the grid changes.

(id: string) => void

-

Grid.Dropdown

A dropdown menu that will be shown in the right-hand-side of the search bar.

Example

import { Grid, Image } from "@raycast/api";
import { useState } from "react";

const types = [
  { id: 1, name: "Smileys", value: "smileys" },
  { id: 2, name: "Animals & Nature", value: "animals-and-nature" },
];

const items: { [key: string]: { content: Image.ImageLike; keywords: string[] }[] } = {
  smileys: [{ content: "🥳", keywords: ["partying", "face"] }],
  "animals-and-nature": [{ content: "🙈", keywords: ["see-no-evil", "monkey"] }],
};

export default function Command() {
  const [type, setType] = useState<string>("smileys");

  return (
    <Grid
      navigationTitle="Search Beers"
      searchBarPlaceholder="Search your favorite drink"
      searchBarAccessory={
        <Grid.Dropdown tooltip="Select Emoji Category" storeValue={true} onChange={(newValue) => setType(newValue)}>
          <Grid.Dropdown.Section title="Emoji Categories">
            {types.map((type) => (
              <Grid.Dropdown.Item key={type.id} title={type.name} value={type.value} />
            ))}
          </Grid.Dropdown.Section>
        </Grid.Dropdown>
      }
    >
      {(items[type] || []).map((item) => (
        <Grid.Item key={`${item.content}`} content={item.content} keywords={item.keywords} />
      ))}
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

tooltip*

Tooltip displayed when hovering the dropdown.

string

-

children

Dropdown sections or items. If Dropdown.Item elements are specified, a default section is automatically created.

React.ReactNode

-

defaultValue

The default value of the dropdown. Keep in mind that defaultValue will be configured once per component lifecycle. This means that if a user changes the value, defaultValue won't be configured on re-rendering.

string

-

filtering

Toggles Raycast filtering. When true, Raycast will use the query in the search bar to filter the items. When false, the extension needs to take care of the filtering.

boolean or { keepSectionOrder: boolean }

false when onSearchTextChange is specified, true otherwise.

id

ID of the dropdown.

string

-

isLoading

Indicates whether a loading indicator should be shown or hidden next to the search bar

boolean

false

placeholder

Placeholder text that will be shown in the dropdown search field.

string

"Search…"

storeValue

Indicates whether the value of the dropdown should be persisted after selection, and restored next time the dropdown is rendered.

boolean

-

throttle

Defines whether the onSearchTextChange handler will be triggered on every keyboard press or with a delay for throttling the events. Recommended to set to true when using custom filtering logic with asynchronous operations (e.g. network requests).

boolean

false

value

The currently value of the dropdown.

string

-

onChange

Callback triggered when the dropdown selection changes.

(newValue: string) => void

-

onSearchTextChange

Callback triggered when the search bar text changes.

(text: string) => void

-

Grid.Dropdown.Item

A dropdown item in a Grid.Dropdown

Example

import { Grid } from "@raycast/api";

export default function Command() {
  return (
    <Grid
      searchBarAccessory={
        <Grid.Dropdown tooltip="Dropdown With Items">
          <Grid.Dropdown.Item title="One" value="one" />
          <Grid.Dropdown.Item title="Two" value="two" />
          <Grid.Dropdown.Item title="Three" value="three" />
        </Grid.Dropdown>
      }
    >
      <Grid.Item content="https://placekitten.com/400/400" title="Item in the Main Grid" />
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

title*

The title displayed for the item.

string

-

value*

Value of the dropdown item. Make sure to assign each unique value for each item.

string

-

icon

An optional icon displayed for the item.

-

keywords

An optional property used for providing additional indexable strings for search. When filtering the items in Raycast, the keywords will be searched in addition to the title.

string[]

The title of its section if any

Grid.Dropdown.Section

Visually separated group of dropdown items.

Use sections to group related menu items together.

Example

import { Grid } from "@raycast/api";

export default function Command() {
  return (
    <Grid
      searchBarAccessory={
        <Grid.Dropdown tooltip="Dropdown With Sections">
          <Grid.Dropdown.Section title="First Section">
            <Grid.Dropdown.Item title="One" value="one" />
          </Grid.Dropdown.Section>
          <Grid.Dropdown.Section title="Second Section">
            <Grid.Dropdown.Item title="Two" value="two" />
          </Grid.Dropdown.Section>
        </Grid.Dropdown>
      }
    >
      <Grid.Item content="https://placekitten.com/400/400" title="Item in the Main Grid" />
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

children

The item elements of the section.

React.ReactNode

-

title

Title displayed above the section

string

-

Grid.EmptyView

A view to display when there aren't any items available. Use to greet users with a friendly message if the extension requires user input before it can show any items e.g. when searching for an image, a gif etc.

Raycast provides a default EmptyView that will be displayed if the Grid component either has no children, or if it has children, but none of them match the query in the search bar. This too can be overridden by passing an empty view alongside the other Grid.Items.

Note that the EmptyView is never displayed if the Grid's isLoading property is true and the search bar is empty.

Example

import { useEffect, useState } from "react";
import { Grid, Image } from "@raycast/api";

export default function CommandWithCustomEmptyView() {
  const [state, setState] = useState<{
    searchText: string;
    items: { content: Image.ImageLike; title: string }[];
  }>({ searchText: "", items: [] });

  useEffect(() => {
    console.log("Running effect after state.searchText changed. Current value:", JSON.stringify(state.searchText));
    // perform an API call that eventually populates `items`.
  }, [state.searchText]);

  return (
    <Grid onSearchTextChange={(newValue) => setState((previous) => ({ ...previous, searchText: newValue }))}>
      {state.searchText === "" && state.items.length === 0 ? (
        <Grid.EmptyView icon={{ source: "https://placekitten.com/500/500" }} title="Type something to get started" />
      ) : (
        state.items.map((item, index) => <Grid.Item key={index} content={item.content} title={item.title} />)
      )}
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

actions

A reference to an ActionPanel.

React.ReactNode

-

description

An optional description for why the empty view is shown.

string

-

icon

An icon displayed in the center of the EmptyView.

-

title

The main title displayed for the Empty View.

string

-

Grid.Item

A item in the Grid.

This is one of the foundational UI components of Raycast. A grid item represents a single entity. It can be an image, an emoji, a GIF etc. You most likely want to perform actions on this item, so make it clear to the user what this item is about.

Example

import { Grid } from "@raycast/api";

export default function Command() {
  return (
    <Grid>
      <Grid.Item content="🥳" title="Partying Face" subtitle="Smiley" />
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

content*

An image or color, optionally with a tooltip, representing the content of the grid item.

Image.ImageLike or { color: Color.ColorLike } or { tooltip: string; value: Image.ImageLike or { color: Color.ColorLike } }

-

accessory

An optional Grid.Item.Accessory item displayed underneath a Grid.Item.

-

actions

An ActionPanel that will be updated for the selected grid item.

React.ReactNode

-

id

ID of the item. This string is passed to the onSelectionChange handler of the Grid when the item is selected. Make sure to assign each item a unique ID or a UUID will be auto generated.

string

-

keywords

An optional property used for providing additional indexable strings for search. When filtering the list in Raycast through the search bar, the keywords will be searched in addition to the title.

string[]

-

quickLook

Optional information to preview files with Quick Look. Toggle the preview ith Action.ToggleQuickLook.

{ name?: string; path: string }

-

subtitle

An optional subtitle displayed below the title.

string

-

title

An optional title displayed below the content.

string

-

Grid.Section

A group of related Grid.Item.

Sections are a great way to structure your grid. For example, you can group photos taken in the same place or in the same day. This way, the user can quickly access what is most relevant.

Sections can specify their own columns, fit, aspectRatio and inset props, separate from what is defined on the main Grid component.

Example

import { Grid } from "@raycast/api";

export default function Command() {
  return (
    <Grid>
      <Grid.Section title="Section 1">
        <Grid.Item content="https://placekitten.com/400/400" title="Item 1" />
      </Grid.Section>
      <Grid.Section title="Section 2" subtitle="Optional subtitle">
        <Grid.Item content="https://placekitten.com/400/400" title="Item 1" />
      </Grid.Section>
    </Grid>
  );
}

Props

PropDescriptionTypeDefault

aspectRatio

Aspect ratio for the Grid.Item elements. Defaults to 1.

"1" or "3/2" or "2/3" or "4/3" or "3/4" or "16/9" or "9/16"

-

children

The Grid.Item elements of the section.

React.ReactNode

-

columns

Column count for the section. Minimum value is 1, maximum value is 8.

number

5

fit

Fit for the Grid.Item element content. Defaults to "contain"

-

inset

Inset for the Grid.Item element content. Defaults to "none".

-

subtitle

An optional subtitle displayed next to the title of the section.

string

-

title

Title displayed above the section.

string

-

Types

Grid.Item.Accessory

An interface describing an accessory view in a Grid.Item.

Grid.Inset

An enum representing the amount of space there should be between a Grid Item's content and its borders. The absolute value depends on the value of Grid's or Grid.Section's columns prop.

Enumeration members

NameDescription

Small

Small insets

Medium

Medium insets

Large

Large insets

Grid.ItemSize (deprecated)

An enum representing the size of the Grid's child Grid.Items.

Enumeration members

NameDescription

Small

Fits 8 items per row.

Medium

Fits 5 items per row.

Large

Fits 3 items per row.

Grid.Fit

An enum representing how Grid.Item's content should be fit.

Enumeration members

NameDescription

Contain

The content will be contained within the grid cell, with vertical/horizontal bars if its aspect ratio differs from the cell's.

Fill

The content will be scaled proportionally so that it fill the entire cell; parts of the content could end up being cropped out.

Last updated