Previously, you set up the foundation of your app. You're now ready to add app components that can help merchants create their pre-orders.
On the app page, you want to add functionality that enables merchants to do the following tasks:
- Set the pre-order name
- Configure the delivery, inventory, and billing policies for the pre-order
- Select products that will have the pre-order
You'll use [Polaris](https://polaris.shopify.com) and [App Bridge React](/docs/api/app-bridge/previous-versions/app-bridge-from-npm/using-react) to build the user interface. You'll use GraphQL Admin API mutations and queries to create and retrieve pre-orders.
At the end of this tutorial, you'll have an understanding on how to create pre-orders. This app will be simple, but you’ll learn where to find resources to build more complex features on your own.
> Note:
> The sample app in this tutorial is assigned with a `PRE_ORDER` category. When you create your own app, you can assign a category based on the app's purpose. For more information, refer to [Selling plans category](/docs/apps/build/purchase-options/deferred#selling-plan-categories).
## What you'll learn
In this tutorial, you'll learn how to do the following tasks:
- Add new routes to your app's server that call the Shopify Admin API
- Add UI to create a pre-order
- Test your app on a development store
This tutorial is not intended to give you a full example on how to build a pre-order application, but more a reference starter.
## Requirements
- Complete the [Getting started](/docs/apps/build/purchase-options/deferred/create-deferred-purchase-app/start-building) tutorial to set up the foundation of your app.
- Familiarize yourself with [selling plans](/docs/apps/build/purchase-options) and [pre-orders and try before you buy](/docs/apps/build/purchase-options/deferred).
## Step 1: Create the pre-order creation route
Create a file `app.create.jsx` under `app/routes`. This file will create the route `/app/create` that will let you create a pre-order.
## Step 2: Create app components
Create the following app components:
- [name](#create-the-name-component)
- [checkout charge](#create-the-checkout-charge-component)
- [product picker](#create-the-product-picker-component)
> Tip:
> We recommend keeping your app component files in the `app/components` folder.
### Create the name component
Create a component that enables merchants to set the pre-order name. The name component includes the following Polaris components:
- [`Card`](https://polaris.shopify.com/components/layout-and-structure/card)
- [`TextField`](https://polaris.shopify.com/components/selection-and-input/text-field)

In your `app/routes/app.create.jsx` file, add the following code:
```jsx
import { useState } from "react";
import {
Page,
Card,
BlockStack,
Layout,
TextField,
FormLayout,
} from "@shopify/polaris";
export default function Index() {
const [sellingPlanName, setSellingPlanName] = useState("");
return (
<>
>
);
}
```
### Create the checkout charge component
When merchants create a pre-order, they can decide the initial charge when customers check out. The type of charge can be set with a `PERCENTAGE` or `PRICE` type. Learn more about [selling plan checkout charge types](/docs/api/admin-graphql/latest/enums/SellingPlanCheckoutChargeType) and [selling plan checkout charge values](/docs/api/admin-graphql/2024-01/unions/SellingPlanCheckoutChargeValue).
The checkout charge component includes the following Polaris components:
- [`Card`](https://polaris.shopify.com/components/layout-and-structure/card)
- [`TextField`](https://polaris.shopify.com/components/selection-and-input/text-field)
- [`Date picker`](https://polaris.shopify.com/components/selection-and-input/date-picker)

In the `app/components` folder, create the file `checkoutCharge.jsx`.
```jsx
import {
Text,
Card,
BlockStack,
DatePicker,
TextField,
} from "@shopify/polaris";
import { useState } from "react";
export default function CheckoutCharge({
selectedDates,
setSelectedDates,
initialCheckoutCharge,
setInitialCheckoutCharge,
}) {
const today = new Date();
const [{ month, year }, setDate] = useState({
month: today.getMonth(),
year: today.getFullYear(),
});
const handleMonthChange = (month, year) => setDate({ month, year });
return (
{initialCheckoutCharge < 100 && (
Remaining balance charge date
)}
);
}
```
### Create the product picker component
Create a component that enables merchants to select products that will have the pre-order. The product selection component includes the [Resource Picker API](/docs/api/app-bridge-library/apis/resource-picker).

In the `app/components` folder, create the file `ProductPicker.jsx`.
```jsx
import { Link } from "@remix-run/react";
import {
Text,
Card,
BlockStack,
Button,
TextField,
Icon,
InlineStack,
Thumbnail,
Box,
} from "@shopify/polaris";
import { SearchIcon, ImageIcon } from "@shopify/polaris-icons";
export default function ProductPicker({
selectedProducts,
setSelectedProducts,
}) {
async function selectProducts(selectedProducts, searchQuery) {
const selectedItems = await window.shopify.resourcePicker({
selectionIds: selectedProducts,
multiple: true,
query: searchQuery,
type: "product",
action: "select",
});
if (selectedItems) {
setSelectedProducts(selectedItems);
}
}
return (
selectProducts(selectedProducts)}
>
Edit
);
},
)}
) : null}
);
}
```
## Step 3: Action to create the pre-order
The next step is to create the `action` that will make it possible to create the pre-order with all the information we are gathering with the previous created components. Add the action inside your `app/routes/app.create.jsx` file. The following action is currently showing a working example on how to use the [sellingPlanGroupCreate](/docs/api/admin-graphql/latest/mutations/sellingPlanGroupCreate) mutation to create a pre-order, this example can also be updated to create a Try before you buy.
```jsx
export const action = async ({ request }) => {
const { admin } = await authenticate.admin(request);
const form = await request.formData();
const sellingPlanName = form.get("sellingPlanName");
const initialCheckoutCharge = form.get("initialCheckoutCharge");
const selectedProducts = form.get("selectedProducts");
const selectedProductsArray = selectedProducts
? selectedProducts.split(",")
: [];
const selectedDates = form.get("selectedDates");
const haveRemainingBalance = Number(initialCheckoutCharge) < 100;
const response = await admin.graphql(
`#graphql
mutation sellingPlanGroupCreate($input: SellingPlanGroupInput!, $resources: SellingPlanGroupResourceInput!) {
sellingPlanGroupCreate(input: $input, resources: $resources) {
sellingPlanGroup {
id
}
userErrors {
field
message
}
}
}`,
{
variables: {
input: {
name: sellingPlanName,
merchantCode: "Pre-order",
options: ["pre-order"],
position: 1,
sellingPlansToCreate: [
{
name: "Pre-order with deposit",
options: "Pre-order with deposit",
category: "PRE_ORDER",
billingPolicy: {
fixed: {
checkoutCharge: {
type: "PERCENTAGE",
value: {
percentage: Number(initialCheckoutCharge),
},
},
remainingBalanceChargeTrigger: haveRemainingBalance
? "EXACT_TIME"
: "NO_REMAINING_BALANCE",
remainingBalanceChargeExactTime: haveRemainingBalance
? new Date(selectedDates).toISOString()
: null,
},
},
deliveryPolicy: {
fixed: {
fulfillmentTrigger: "UNKNOWN",
},
},
inventoryPolicy: {
reserve: "ON_FULFILLMENT",
},
},
],
},
resources: {
productIds: selectedProductsArray,
},
},
},
);
const responseJson = await response.json();
return json({
sellingPlanGroup:
responseJson.data?.sellingPlanGroupCreate?.sellingPlanGroup?.id,
});
};
```
## Step 4: Complete create page
The following code illustrate the complete code for the `app/routes/app.create.jsx` file. We use the [Toast API](/docs/api/app-bridge-library/apis/toast) to add feedback when a merchant is creating a new pre-order.
```jsx
import { useState, useEffect } from "react";
import { json } from "@remix-run/node";
import { useActionData, useNavigation, useSubmit } from "@remix-run/react";
import {
Page,
Card,
BlockStack,
Button,
PageActions,
Layout,
TextField,
FormLayout,
} from "@shopify/polaris";
import { authenticate } from "../shopify.server";
import ProductPicker from "../components/ProductPicker";
import CheckoutCharge from "../components/CheckoutCharge";
export const action = async ({ request }) => {
const { admin } = await authenticate.admin(request);
const form = await request.formData();
const sellingPlanName = form.get("sellingPlanName");
const initialCheckoutCharge = form.get("initialCheckoutCharge");
const selectedProducts = form.get("selectedProducts");
const selectedProductsArray = selectedProducts
? selectedProducts.split(",")
: [];
const selectedDates = form.get("selectedDates");
const haveRemainingBalance = Number(initialCheckoutCharge) < 100;
const response = await admin.graphql(
`#graphql
mutation sellingPlanGroupCreate($input: SellingPlanGroupInput!, $resources: SellingPlanGroupResourceInput!) {
sellingPlanGroupCreate(input: $input, resources: $resources) {
sellingPlanGroup {
id
}
userErrors {
field
message
}
}
}`,
{
variables: {
input: {
name: sellingPlanName,
merchantCode: "Pre-order",
options: ["pre-order"],
position: 1,
sellingPlansToCreate: [
{
name: "Pre-order with deposit",
options: "Pre-order with deposit",
category: "PRE_ORDER",
billingPolicy: {
fixed: {
checkoutCharge: {
type: "PERCENTAGE",
value: {
percentage: Number(initialCheckoutCharge),
},
},
remainingBalanceChargeTrigger: haveRemainingBalance
? "EXACT_TIME"
: "NO_REMAINING_BALANCE",
remainingBalanceChargeExactTime: haveRemainingBalance
? new Date(selectedDates).toISOString()
: null,
},
},
deliveryPolicy: {
fixed: {
fulfillmentTrigger: "UNKNOWN",
},
},
inventoryPolicy: {
reserve: "ON_FULFILLMENT",
},
},
],
},
resources: {
productIds: selectedProductsArray,
},
},
},
);
const responseJson = await response.json();
return json({
sellingPlanGroup:
responseJson.data?.sellingPlanGroupCreate?.sellingPlanGroup?.id,
});
};
export default function Index() {
const nav = useNavigation();
const actionData = useActionData();
const submit = useSubmit();
const [selectedProducts, setSelectedProducts] = useState([]);
const [sellingPlanName, setSellingPlanName] = useState("");
const [initialCheckoutCharge, setInitialCheckoutCharge] = useState(0);
const handleSellingPlanNameChange = (newValue) =>
setSellingPlanName(newValue);
const isLoading =
["loading", "submitting"].includes(nav.state) && nav.formMethod === "POST";
useEffect(() => {
if (actionData?.sellingPlanGroup) {
shopify.toast.show("Pre-order created", {
isError: false,
});
}
}, [actionData]);
const today = new Date();
const [selectedDates, setSelectedDates] = useState({
start: today,
end: today,
});
const createPreorder = () =>
submit(
{
selectedProducts: selectedProducts.map((product) => product.id),
sellingPlanName,
initialCheckoutCharge,
selectedDates: initialCheckoutCharge < 100 ? selectedDates.start : null,
},
{ replace: true, method: "POST" },
);
return (
<>
createPreorder()}
>
Create
}
/>
>
);
}
```
## Next steps
The code that you just created is only a first step to create a complete pre-order or Try before you buy app. You can use the following API object to improve your application:
- [SellingPlanCheckoutCharge](docs/api/admin-graphql/2024-01/objects/SellingPlanCheckoutCharge) that will let you decide how customers are charged with pre-order and TBYB.
- [SellingPlanFixedBillingPolicy](/docs/api/admin-graphql/2024-01/objects/SellingPlanFixedBillingPolicy) that will let you decide how customers will handle the remain balance customer will need to pay for a pre-order and TBYB
- [SellingPlanFixedDeliveryPolicy](/docs/api/admin-graphql/2024-01/objects/SellingPlanFixedDeliveryPolicy) that will let you decide how fulfillment will work with you pre-order and TBYB.
- [SellingPlanInventoryPolicy](/docs/api/admin-graphql/2024-01/objects/SellingPlanInventoryPolicy) that will let you decide how the inventory will update with you pre-order and TBYB.