diff --git a/app/(page)/product/[handle]/page.js b/app/(page)/product/[handle]/page.js
index c4932e8f3..aa43f46b3 100644
--- a/app/(page)/product/[handle]/page.js
+++ b/app/(page)/product/[handle]/page.js
@@ -1,3 +1,5 @@
+import Image from 'next/image';
+
 import xss from 'xss';
 
 import { getProducts, getProduct } from 'lib/shopify';
@@ -18,6 +20,7 @@ export async function generateStaticParams() {
 
 export default async function ProductPage({ params: { handle } }) {
     const product = await getProduct(handle);
+
     return (
         <>
             {product?.handle ? (
@@ -33,6 +36,16 @@ export default async function ProductPage({ params: { handle } }) {
             ) : (
                 <p>Product not found</p>
             )}
+            <p>Scroll to right ( → )</p>
+            {product?.images?.map(image => (
+                <Image
+                    key={image?.url}
+                    src={image?.url}
+                    alt={image?.altText}
+                    width={image?.width}
+                    height={image?.height}
+                />
+            ))}
         </>
     );
 }
diff --git a/components/home.js b/components/home.js
index e6d7d5fa7..a5bf76c0e 100644
--- a/components/home.js
+++ b/components/home.js
@@ -1,61 +1,11 @@
+import 'server-only';
+
 import Link from 'next/link';
 import Image from 'next/image';
 
 import { getCollectionProducts, getMenu } from 'lib/shopify';
 
-export const formatPrice = ({ amount, currencyCode }) => {
-    const USDollar = new Intl.NumberFormat('en-US', {
-        style: 'currency',
-        currency: currencyCode,
-    });
-
-    return USDollar.format(amount);
-};
-
-export const formatPriceRange = ({ maxVariantPrice, minVariantPrice }) => {
-    if (maxVariantPrice.amount == minVariantPrice.amount) {
-        return `${formatPrice(maxVariantPrice)}`;
-    } else {
-        return `${formatPrice(minVariantPrice)} - ${formatPrice(
-            maxVariantPrice
-        )}`;
-    }
-};
-
-export const PriceRanges = ({
-    priceRange,
-    compareAtPriceRange,
-    availableForSale,
-}) => {
-    //TODO: turn these checks into shared functions
-    const onSale =
-        (compareAtPriceRange?.minVariantPrice?.amount ?? 0) >
-            (priceRange?.minVariantPrice?.amount ?? 0) ||
-        (compareAtPriceRange?.maxVariantPrice?.amount ?? 0) >
-            (priceRange?.maxVariantPrice?.amount ?? 0);
-    const isForSale = (priceRange?.maxVariantPrice?.amount ?? 0) > 0;
-
-    return (
-        <p>
-            {availableForSale ? (
-                isForSale && (
-                    <>
-                        <>
-                            {onSale && (
-                                <span className={'original-price'}>
-                                    {formatPriceRange(compareAtPriceRange)}{' '}
-                                </span>
-                            )}
-                        </>
-                        <span>{formatPriceRange(priceRange)}</span>
-                    </>
-                )
-            ) : (
-                <span>Sold Out</span>
-            )}
-        </p>
-    );
-};
+import { PriceRanges } from '/components/price.js';
 
 export async function HomeProduct({ product }) {
     const featuredImage = product?.images?.[0];
@@ -75,7 +25,7 @@ export async function HomeProduct({ product }) {
                     ?.map(collection => collection?.title)
                     .join(', ')})`}</p>
             )}
-            <PriceRanges {...product} />
+            <PriceRanges product={product} />
         </Link>
     );
 }
diff --git a/components/input.js b/components/input.js
index 92f4059d2..fd5777d11 100644
--- a/components/input.js
+++ b/components/input.js
@@ -9,7 +9,7 @@ export function Select({ id, label, children, ...props }) {
                 {children}
             </select>
             {/* TODO: parentheses around label w/ css */}
-            <label for={id}>{label}</label>
+            <label htmlFor={id}>{label}</label>
         </div>
     );
 }
@@ -18,7 +18,7 @@ export function NumberInput({ id, label, ...props }) {
     return (
         <div>
             <input {...props} type='number' id={id} name={label} />
-            <label for={id}>{label}</label>
+            <label htmlFor={id}>{label}</label>
         </div>
     );
 }
diff --git a/components/price.js b/components/price.js
new file mode 100644
index 000000000..152ac68b9
--- /dev/null
+++ b/components/price.js
@@ -0,0 +1,103 @@
+export const formatPrice = ({ amount, currencyCode }) => {
+    const USDollar = new Intl.NumberFormat('en-US', {
+        style: 'currency',
+        currency: currencyCode,
+    });
+
+    return USDollar.format(amount);
+};
+
+export const formatPriceRange = ({ maxVariantPrice, minVariantPrice }) => {
+    if (maxVariantPrice.amount == minVariantPrice.amount) {
+        return `${formatPrice(maxVariantPrice)}`;
+    } else {
+        return `${formatPrice(minVariantPrice)} - ${formatPrice(
+            maxVariantPrice
+        )}`;
+    }
+};
+
+//TODO: might be safer not to destructure keys from `product`, use nullish coalescing instead
+export const productAvailableForSale = product =>
+    product?.availableForSale ?? false;
+export const productOnSale = product =>
+    (product?.compareAtPriceRange?.minVariantPrice?.amount ?? 0) >
+        (product?.priceRange?.minVariantPrice?.amount ?? 0) ||
+    (product?.compareAtPriceRange?.maxVariantPrice?.amount ?? 0) >
+        (product?.priceRange?.maxVariantPrice?.amount ?? 0);
+export const productIsForSale = product =>
+    (product?.priceRange?.maxVariantPrice?.amount ?? 0) > 0;
+export const productHasOptions = product =>
+    product?.options?.[0]?.values?.length > 1 ?? false;
+
+export const PriceRanges = ({ product }) => {
+    const availableForSale = productAvailableForSale(product);
+    const onSale = productOnSale(product);
+    const isForSale = productIsForSale(product);
+
+    return (
+        <p>
+            {availableForSale ? (
+                isForSale && (
+                    <>
+                        <>
+                            {onSale && (
+                                <span className={'original-price'}>
+                                    {formatPriceRange(
+                                        product?.compareAtPriceRange
+                                    )}{' '}
+                                </span>
+                            )}
+                        </>
+                        <span>{formatPriceRange(product?.priceRange)}</span>
+                    </>
+                )
+            ) : (
+                <span>Sold Out</span>
+            )}
+        </p>
+    );
+};
+
+export const variantAvailableForSale = variant =>
+    variant?.availableForSale ?? false;
+export const variantOnSale = variant =>
+    (variant?.compareAtPrice?.amount ?? 0) > (variant?.price?.amount ?? 0);
+
+export const VariantPrice = ({ variant, quantity }) => {
+    const availableForSale = variantAvailableForSale(variant);
+    const onSale = variantOnSale(variant);
+
+    return variant ? (
+        <div>
+            {availableForSale ? (
+                <>
+                    <>
+                        {onSale && (
+                            <p className={'original-price'}>
+                                {formatPrice({
+                                    amount:
+                                        (variant?.compareAtPrice?.amount ?? 0) *
+                                        quantity,
+                                    currencyCode:
+                                        variant?.compareAtPrice?.currencyCode,
+                                })}
+                            </p>
+                        )}
+                    </>
+                    <p>
+                        {formatPrice({
+                            amount: (variant?.price?.amount ?? 0) * quantity,
+                            currencyCode: variant?.price?.currencyCode,
+                        })}
+                    </p>
+                </>
+            ) : (
+                // TODO: this can just say "Sold Out" in the future
+                <p>Variant Sold Out</p>
+            )}
+        </div>
+    ) : (
+        <p>Sorry, the price can't be found</p>
+    );
+};
diff --git a/components/product/purchase-input.js b/components/product/purchase-input.js
index 212cbc37d..afd14b320 100644
--- a/components/product/purchase-input.js
+++ b/components/product/purchase-input.js
@@ -1,43 +1,120 @@
 'use client';
 
+import { useState } from 'react';
+import Link from 'next/link';
+
 import { Option, Select, NumberInput } from '/components/input.js';
+import {
+    productAvailableForSale,
+    productHasOptions,
+    productIsForSale,
+    VariantPrice,
+} from '/components/price.js';
+
+export const productVariant = ({ product, selectedOptions }) => {
+    const hasOptions = productHasOptions(product);
+
+    if (hasOptions) {
+        const optionNames =
+            product?.options?.map(option => option?.name ?? '') ?? [];
+
+        console.log({
+            product: product?.handle,
+            optionNames,
+            variants: product?.variants,
+        });
+
+        for (const variant of product?.variants ?? []) {
+            let matching = true;
+
+            console.log({ variantTitle: variant?.title });
+
+            for (const option of variant?.selectedOptions) {
+                const optionName = option?.name ?? '';
+                const optionValue = option?.value ?? '';
+
+                console.log({ optionName, optionValue, optionNames });
+
+                for (let i = 0; i < optionNames?.length; i++) {
+                    if (optionName == optionNames[i]) {
+                        console.log({
+                            optionName,
+                            optionValue,
+                            selectedOption: selectedOptions[i],
+                        });
+
+                        if (optionValue != selectedOptions[i]) {
+                            matching = false;
+                        }
+                    }
+                }
+            }
+
+            if (matching) {
+                return variant;
+            }
+        }
+    }
+};
 
 export default function PurchaseInput({ product }) {
-    const hasOptions = product?.options?.[0]?.values.length > 1 ?? false;
-    //TODO: turn these checks into shared functions
-    // const onSale =
-    //     (compareAtPriceRange?.minVariantPrice?.amount ?? 0) >
-    //         (priceRange?.minVariantPrice?.amount ?? 0) ||
-    //     (compareAtPriceRange?.maxVariantPrice?.amount ?? 0) >
-    //         (priceRange?.maxVariantPrice?.amount ?? 0);
-    const isForSale = (product?.priceRange?.maxVariantPrice?.amount ?? 0) > 0;
+    const hasOptions = productHasOptions(product);
+    const isForSale = productIsForSale(product);
+    const availableForSale = productAvailableForSale(product);
 
-    return (
-        product?.availableForSale &&
+    const [qty, setQty] = useState(1);
+
+    const [selectedOptions, setSelectedOptions] = useState(
+        product?.options?.map(option => option?.values?.[0] ?? '') ?? []
+    );
+
+    const variant = hasOptions
+        ? productVariant({ product, selectedOptions })
+        : product?.variants?.[0];
+
+    console.log({ variant });
+
+    return availableForSale ? (
         isForSale && (
             <>
-                <NumberInput min='1' value='1' id='quantity' label='Qty' />
+                <NumberInput
+                    min='1'
+                    value={qty}
+                    id='quantity'
+                    label='Qty'
+                    onChange={e => setQty(e.target.value)}
+                />
                 <>
                     {hasOptions &&
-                        product?.options?.map(option => (
+                        product?.options?.map((option, i) => (
                             <Select
                                 key={option?.id}
                                 id={option?.name}
                                 label={option?.name}
+                                value={selectedOptions[i]}
+                                onChange={e =>
+                                    setSelectedOptions(
+                                        selectedOptions.map((value, ii) =>
+                                            i == ii ? e.target.value : value
+                                        )
+                                    )
+                                }
                             >
-                                {option?.values?.map((value, i) => (
-                                    <Option
-                                        key={value}
-                                        value={value}
-                                        selected={i == 0}
-                                    >
+                                {option?.values?.map(value => (
+                                    <Option key={value} value={value}>
                                         {value}
                                     </Option>
                                 ))}
                             </Select>
                         ))}
                 </>
+                <VariantPrice variant={variant} quantity={qty} />
+                {/* TODO: add to cart on click */}
+                <button type='button'>Buy Now!</button>
+                <Link href='/checkout'>Checkout?</Link>
             </>
         )
+    ) : (
+        <p>Sold Out</p>
     );
 }
diff --git a/lib/shopify/fragments/product.ts b/lib/shopify/fragments/product.ts
index e6ea44cf4..c4df506ae 100644
--- a/lib/shopify/fragments/product.ts
+++ b/lib/shopify/fragments/product.ts
@@ -38,6 +38,10 @@ const productFragment = /* GraphQL */ `
                         amount
                         currencyCode
                     }
+                    compareAtPrice {
+                        amount
+                        currencyCode
+                    }
                 }
             }
         }
diff --git a/package-lock.json b/package-lock.json
index 74feeadb4..eededf34d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
                 "react": "18.2.0",
                 "react-cookie": "^4.1.1",
                 "react-dom": "18.2.0",
+                "server-only": "^0.0.1",
                 "xss": "^1.0.14"
             },
             "devDependencies": {
@@ -5157,6 +5158,11 @@
                 "node": ">=10"
             }
         },
+        "node_modules/server-only": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
+            "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
+        },
         "node_modules/shebang-command": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/package.json b/package.json
index 669b9196f..f56cb15d3 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
         "react": "18.2.0",
         "react-cookie": "^4.1.1",
         "react-dom": "18.2.0",
+        "server-only": "^0.0.1",
         "xss": "^1.0.14"
     },
     "devDependencies": {