Skip to main content

How to show search page results as grid?

If you want to show search results as a grid, you can do this by replacing the search page with a custom implementation that uses the dataSource pattern.

Prerequisites

Before implementing this customization, ensure:

  • Your theme extends @appmaker-xyz/shopify-core-theme
  • You have zod installed in your project (optional, for param validation):
    yarn add zod

Folder Structure

Your theme should have the following structure:

src/
├── pages/
│ ├── index.js # Exports pages object
│ └── searchPage.js # Your custom search page
└── ...

Implementation

This approach uses the dataSource attribute with repeatable: 'Yes' to fetch and display search results in a grid layout.

Steps

  1. Create a new file in the src/pages folder of your theme and name it searchPage.js (or searchPage.ts for TypeScript).

  2. Add the following code to the file:

import z from 'zod';

const validQueryParamsSchema = z.string().min(1);

const searchPage = {
title: 'Search',
attributes: {
headerShown: false,
backgroundColor: '#fff',
contentContainerStyle: {
paddingHorizontal: 24,
},
},
stickyHeader: {
blocks: [
{
name: 'shopify/search-block',
attributes: {},
},
],
},
blocks: [
// Show suggestions when search query is empty
{
name: 'shopify/search-suggest',
attributes: {
__display: '{{lodash.isEmpty(pageState.searchInputValue)}}',
},
},
{
name: 'shopify/collection-suggest',
attributes: {
__display:
'{{lodash.isEmpty(pageState.searchInputValue) && !checkIfTrueFalse(plugins.shopify.settings.hide_collection_suggest)}}',
},
},
// Grid display when search query exists
{
name: 'appmaker/product-grid-item',
clientId: 'search-result-grid',
attributes: {
__display: '{{!lodash.isEmpty(pageState.searchInputValue)}}',
gridViewListing: true,
numColumns: 2,
hasPages: false,
dataSource: {
source: 'shopify',
responseType: 'replace',
attributes: {
mapping: {
items: 'data.data.products.edges',
},
methodName: 'searchProduct',
params: '{{pageState.searchInputValue}}',
// Optional: Validates params before fetching
paramsValidations: {
zodSchema: validQueryParamsSchema,
},
},
repeatable: 'Yes',
repeatItem: 'DataSource',
dependencies: {
pageState: ['searchInputValue'],
},
},
},
dependencies: {
pageState: ['searchInputValue'],
},
},
],
};

export default searchPage;
  1. Import the searchPage file in the src/pages/index.js (or index.ts) file and add it to the pages object:
import searchPage from './searchPage';

const pages = {
// other pages
searchPage,
};

export { pages };
  1. Now, when you search for a product, the search results will be displayed as a grid.

Screenshot

Here's what the search page looks like with grid layout on mobile:

App Screenshot

Key Attributes Reference

Page Attributes

AttributeTypeDescription
headerShownbooleanShow/hide the navigation header
backgroundColorstringPage background color
contentContainerStyleobjectStyle for the content container

Search Block (shopify/search-block)

This block provides the search input and handles the search query state internally via the useSearchQuery hook.

Grid Display Attributes (appmaker/product-grid-item)

AttributeTypeDescription
clientIdstringUnique identifier for the block instance (used for debugging/testing)
gridViewListingbooleanEnable grid layout (set to true)
numColumnsnumberNumber of columns in grid (default: 2)
hasPagesbooleanEnable/disable pagination for infinite scroll (set to false for search results)

DataSource Configuration

AttributeTypeDescription
sourcestringData source identifier ('shopify')
responseTypestringHow to handle response ('replace')
attributes.mapping.itemsstringPath to extract items from response
attributes.methodNamestringMethod to call on data source ('searchProduct')
attributes.paramsstringParameters for the method
attributes.paramsValidationsobject(Optional) Validation rules for params
repeatablestringSet to 'Yes' to enable list rendering
repeatItemstringSet to 'DataSource'
dependencies.pageStatearrayPageState keys to watch for changes

Conditional Display (__display)

Use the __display attribute to conditionally show/hide blocks:

// Show only when searchInputValue is NOT empty
__display: '{{!lodash.isEmpty(pageState.searchInputValue)}}'

// Show only when searchInputValue IS empty
__display: '{{lodash.isEmpty(pageState.searchInputValue)}}'

Validation with Zod (Optional)

The paramsValidations attribute is optional and supports Zod schema validation to ensure search queries meet requirements before fetching.

Note: If using Zod validation, ensure zod is installed: yarn add zod

import z from 'zod';

// Require at least 1 character
const validQueryParamsSchema = z.string().min(1);

// Use in dataSource
paramsValidations: {
zodSchema: validQueryParamsSchema,
}

Other validation options:

// Function-based validation
paramsValidations: {
fn: (params) => params && params.length > 0,
}

// Simple required validation
paramsValidations: {
required: true,
type: 'string',
}

Troubleshooting

Search results not displaying as grid

  1. Ensure gridViewListing: true is set in the block attributes
  2. Verify the block is using dataSource (not customDataSource)
  3. Check that repeatable: 'Yes' is set in the dataSource
  4. Verify you're using searchInputValue (not searchKey) for the pageState dependency

Search not triggering

  1. Verify the block name is correct: shopify/search-block
  2. Ensure params uses the correct pageState key: '{{pageState.searchInputValue}}'

Empty results

  1. Verify the mapping.items path matches your API response structure (data.data.products.edges)
  2. Check that paramsValidations is not blocking valid queries
  3. Ensure the searchProduct method exists on the Shopify data source

Migration from Old Implementation

If you have an existing search page using customDataSource on appmaker/shopify-product-list, migrate as follows:

Before (Does Not Work)

{
name: 'appmaker/shopify-product-list',
attributes: {
customDataSource: { // This is NOT supported
source: 'shopify',
// ...
},
},
}

After (Correct)

{
name: 'appmaker/product-grid-item',
attributes: {
dataSource: { // Use dataSource instead
source: 'shopify',
// ...
repeatable: 'Yes',
},
},
}

Key changes:

  1. Change block from appmaker/shopify-product-list to appmaker/product-grid-item
  2. Change customDataSource to dataSource
  3. Ensure repeatable: 'Yes' is set
  4. Use searchInputValue instead of searchKey for pageState