A Standard Look for Shopify Forms

As codebases grow, teams often converge on shared UI conventions. Shopify’s Polaris design system is the result of that process for the Shopify admin: a set of React components and design guidelines that give internal and third-party developers a consistent toolkit for building interfaces. Polaris includes components for common UI patterns and a set of 16 form-related components covering everything from checkboxes and pickers to text fields.

In practical terms, Polaris form layouts appear throughout the admin—when creating a Product, Collection, Order, or Gift Card, you’re working with a consistent structure built from the same reusable components.

Add Product and Create Collection Forms
Add Product and Create Collection Forms

Short-Term Goal, Broader Lesson

We’ll build a basic form for adding a product, with a Title field, a Description field, a Save button, and a top navigation link back to the previous page. Along the way, the real focus is on React fundamentals: state, events, and event handlers. We’ll style with Polaris, but the core exercise is wiring a functional form, not just static markup.

Add Product Form
Add Product Form

A Starter CodeSandbox is available to fork and follow along. If you’d rather see the end result first, a Solution CodeSandbox is also provided.

Project Structure and Dependencies

The starter code is a standard create-react-app project. We’ll build our UI in a single PolarisForm component, using the @shopify/polaris library already listed in the dependencies. Component folders follow a common pattern: an index.js for exports and a ComponentName.js for the definition, mirroring how Polaris itself organizes its Avatar component and others.

Initial Setup in CodeSandbox
Initial Setup

Selecting the Right Components

At first glance, choosing components feels intuitive, but it often requires digging into Polaris documentation. For our form, we’ll need the following:

Form

The actual form

FormLayout

To apply a bit of styling between the fields

TextField

The text inputs for our form

Page

To provide the back arrow navigation and Save button

Card

To apply a bit of styling around the form

Start With Form

The Form component is a wrapper that handles form submissions. Its documentation includes best practices, related components, and examples, plus references for all available props. We’ll begin by importing Form, FormLayout, TextField, Page, and Card, then render a simple form with two TextField components with label, type, and multiline props.

Immediately, we run into the first real obstacle:

MissingAppProviderError
No i18n was provided. Your application must be wrapped in an <AppProvider> component. See https://polaris.shopify.com/components/structure/app-provider for implementation instructions.

The documentation explains that AppProvider is a required component enabling shared global settings across your application hierarchy. Its most vital job here is distributing translations—the Shopify admin supports up to 20 languages, and every child component needs access to those strings. Fixing this is straightforward: import AppProvider and wrap your application in it, replacing any existing React Fragment. The form renders cleanly.

Rendering The Initial Form
Rendering The Initial Form

Inspecting What Polaris Renders

Once the form is on screen, it’s worth inspecting the output. In browser developer tools, you’ll notice Polaris prefixes all class and ID names with Polaris-, and the rendered HTML hierarchy contains additional wrapper elements beyond what you authored. This is normal behavior—the abstraction layer adds structural elements for styling and accessibility.

The React Developer Tools offer another lens. In the Components tab, you can see the full component tree—from App down to the individual inputs. Notably, you’ll see nested providers for context that children consume without prop drilling. This is an early glimpse into React’s Context API, a deeper topic, but one that Polaris relies on heavily to pass settings like locale and theme.

Form Elements In Developer Tools
Form Elements In Developer Tools

Spacing Fields With FormLayout

Our initial layout renders the fields with no vertical space between the inputs. The FormLayout component is designed to arrange fields with standard spacing—by default fields stack vertically, but horizontal groups are also supported. Wrapping both fields in FormLayout resolves the spacing issue and tightens the layout to the expected design.

Form With Spacing Added
Form With Spacing Added

Building the Page Skeleton

The back arrow and Save button require a Page component. The docs describe it as the outer wrapper for a page, with page titles and associated actions. We’ll give it a title of Add Product and an actions prop with a primary action for the Save button.

Polaris Page Component As Displayed In HTML
Page Component With Props

Grouping With the Card Component

The form now has structure, but lacks the breathing room around its fields. You could add custom CSS, but Polaris’s Card component provides the same effect. It groups similar content for merchants to scan quickly, and the sectioned prop automatically wraps content in a padded section. Adding the component and setting sectioned brings the outer spacing in line with the design reference.

Final Design
Final Design

The basic UI is complete. From here, we can shift attention away from layout and toward wiring up the state and event handlers that turn this static screen into a working React form.

Wiring State and Events

At this point the form renders correctly, but typing into the TextField components captures nothing and the Save button is inert. Behind the scenes, React needs explicit instructions to track input and respond to submission. That means introducing state, event handlers, and events.

In React, forms can be either Controlled or Uncontrolled. For this example we'll build a Controlled form: every keystroke updates state and triggers a re-render of the input with its current value.

Setting Up State and Handlers

Because we have two input fields, we'll create two instances of state. First, import the useState Hook from React. Then instantiate state for both title and description. Each instance requires a state value and a setState function. React enforces that any update to the state value goes through that setter function, never through direct assignment.

Next, build event handler functions for each field. Handlers aren't strictly required, but they are a convention in React: they give you a single place to add extra logic before state changes, and other developers will expect them. With two state values, that means two handlers, plus a third to manage form submission.

Attaching Events

Two events are involved: onChange and onSubmit. Add onChange to both TextField components and wire their corresponding handler functions. Since this is a Controlled form, each field also needs a value prop bound to its matching state value.

With the onChange handlers in place, you can verify that typing into the fields updates state by checking the input behavior. For submission, check the Page component documentation: it exposes an onAction prop, which is the one to use to trigger the onSubmit function when Save is clicked.

Clicking Save should log a SyntheticBaseEvent object to the console, confirming that the event is firing correctly.

Resetting the Form

The final step is clearing both fields after a successful submit, giving the merchant a clean slate for the next product entry.

This walkthrough introduces the core Polaris React components in Shopify's design system. The library is open source on GitHub, and its local development environment uses Storybook, which makes it easy to explore the full component set on your own.