Amplify UI

Next.js

How to get started using Amplify UI with Next.js

Tutorial

In this brief tutorial, we're going to build a basic shopping card component using Next.js and Amplify UI. Then, we'll elaborate on our shopping card to play with the possibilities of using Amplify UI.

App Router

Next.js 13.4+ introduces App Router with the usage of Server Components. Amplify UI components are interactive and designed to work on the client side. To use them inside of Server Components you must wrap them in a Client Component with "use client". For more info, visit Next.js third party package documentation.

If you are using Next.js Pages Router, no changes are required to use Amplify UI components.

Setup and Installation

First, execute the command below in your terminal. When prompted for the name of your project, enter amplify-ui-demo.

npx create-next-app@latest amplify-ui-demo --no-app && cd amplify-ui-demo
npx create-next-app@latest amplify-ui-demo && cd amplify-ui-demo

Then, install the Amplify UI React package:

npm install @aws-amplify/ui-react aws-amplify
yarn add @aws-amplify/ui-react aws-amplify

Basic Demo

Open up the project in your IDE and replace the contents of the pages/index.js with the code below:

import { Button, Flex, Heading, Image, Text } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

export default function Home() {
  return (
    <Flex
      direction={{ base: 'column', large: 'row' }}
      maxWidth="32rem"
      padding="1rem"
      width="100%"
    >
      <Image
        alt="Abstract art"
        height="21rem"
        src="https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987"
        width="100%"
      />
      <Flex justifyContent="space-between" direction="column">
        <Heading level={3}>Abstract art</Heading>
        <Text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat
          sed cras ornare arcu dui. Duis aute irure dolor in reprehenderit in
          voluptate velit esse.
        </Text>
        <Button
          variation="primary"
          onClick={() => alert('Added item to cart!')}
        >
          Add to Cart
        </Button>
      </Flex>
    </Flex>
  );
}
Abstract art

Abstract art

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Duis aute irure dolor in reprehenderit in voluptate velit esse.

Next.js 13.4+ introduced app directory with the usage of Server Components. In this guide we placed "use client" at the top of page.jsx to mark it as a Client Component.

Open up the project in your IDE and replace the contents of the app/page.jsx with the code below:

'use client';

import { Button, Flex, Heading, Image, Text } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

export default function Home() {
  return (
    <Flex
      direction={{ base: 'column', large: 'row' }}
      maxWidth="32rem"
      padding="1rem"
      width="100%"
    >
      <Image
        alt="Abstract art"
        height="21rem"
        src="https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987"
        width="100%"
      />
      <Flex justifyContent="space-between" direction="column">
        <Heading level={3}>Abstract art</Heading>
        <Text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat
          sed cras ornare arcu dui. Duis aute irure dolor in reprehenderit in
          voluptate velit esse.
        </Text>
        <Button
          variation="primary"
          onClick={() => alert('Added item to cart!')}
        >
          Add to Cart
        </Button>
      </Flex>
    </Flex>
  );
}
Abstract art

Abstract art

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Duis aute irure dolor in reprehenderit in voluptate velit esse.

When you start the app (npm run dev or yarn dev), you should see a basic shopping card component.

Advanced Demo

If you’d like to play around with a more comprehensive demo of the Amplify UI library, create a separate file under the pages folder named paintings.js, and copy/paste this code into it:

export const PAINTINGS = [
  {
    title: 'Hallway',
    artist: 'Efe Kurnaz',
    src: 'https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Ac feugiat sed lectus vestibulum.',
    price: '$899.99',
    avgRating: 4.8,
    reviews: 445,
    inStock: true,
    readyForPickup: true,
    bestSeller: true,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Fire and Ice',
    artist: 'Pawel Czerwinski',
    src: 'https://images.unsplash.com/photo-1604871000636-074fa5117945?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, ubique patrioque at qui, modo hinc ne duo, ad consul animal volumus est. Ea quo etiam deleniti, amet singulis in sed. Omnesque lobortis vis ex. Wisi latine splendide vis ei, libris commodo no has.',
    price: '$699.99',
    avgRating: 4.1,
    reviews: 222,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Orange, pink, yellow',
    artist: 'Kseniya Lapteva',
    src: 'https://images.unsplash.com/photo-1629196914375-f7e48f477b6d?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1306',
    description:
      'Lorem ipsum dolor sit amet, cu porro vivendum ius. Ad mei sint homero, cum an soluta epicurei. At pri minimum corrumpit. Minim percipitur eu mei, erant habemus deserunt qui et.',
    price: '$139.99',
    avgRating: 3.5,
    reviews: 142,
    inStock: true,
    readyForPickup: false,
    bestSeller: false,
    isNew: true,
    limitedSupply: false,
  },
  {
    title: 'Melted Purple',
    artist: 'Maria Orlova',
    src: 'https://images.unsplash.com/photo-1549490349-8643362247b5?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, facer nemore ei sea, mea facilis eloquentiam at. Et modus pertinax tincidunt est. Propriae argumentum necessitatibus eos ad.',
    price: '$499.99',
    avgRating: 4.5,
    reviews: 301,
    inStock: true,
    readyForPickup: true,
    bestSeller: true,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Experimental',
    artist: 'Bruno Thethe',
    src: 'https://images.unsplash.com/photo-1550275994-cdc89cd1948f?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, ea probo choro tollit pri, ad pro justo intellegam repudiandae, labores civibus eu quo. Cum latine instructior at, est no odio tibique epicuri.',
    price: '$159.99',
    avgRating: 3.1,
    reviews: 56,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: true,
    limitedSupply: false,
  },
  {
    title: 'Rainbow',
    artist: 'Felix Spiske',
    src: 'https://images.unsplash.com/photo-1543857778-c4a1a3e0b2eb?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1310',
    description:
      'Lorem ipsum dolor sit amet, sea tritani indoctum cu, facilis praesent at qui. Cu cetero veritus vel, et prima erant perfecto vix. Tollit delectus scaevola duo et, inermis sensibus voluptatum cu ius.',
    price: '$799.99',
    avgRating: 4.9,
    reviews: 550,
    inStock: true,
    readyForPickup: false,
    bestSeller: true,
    isNew: false,
    limitedSupply: true,
  },
  {
    title: 'Fearless Hue',
    artist: 'Radienta',
    src: 'https://images.unsplash.com/photo-1579547621113-e4bb2a19bdd6?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=939',
    description:
      'Lorem ipsum dolor sit amet, te labore lucilius pro, te his consul singulis, cu vel unum impedit complectitur. In usu erat dicta doctus, purto aeterno vis te. Facete deterruisset nec id. At omittam antiopam pri.',
    price: '$249.99',
    avgRating: 3.3,
    reviews: 294,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Liquid',
    artist: 'Joel Filipe',
    src: 'https://images.unsplash.com/photo-1485163819542-13adeb5e0068?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, cum cu meliore tacimates, vel no sale maiorum. His cu autem placerat. Homero urbanitas vituperata ad sit, ex mel convenire elaboraret.',
    price: '$549.99',
    avgRating: 4.5,
    reviews: 440,
    inStock: false,
    readyForPickup: false,
    bestSeller: false,
    isNew: false,
    limitedSupply: true,
  },
];

Then, replace the contents of pages/index.js with the code below, restart the app, and experience all the beautiful components in action!

import * as React from 'react';
import {
  Alert,
  Badge,
  Button,
  Card,
  Collection,
  Divider,
  Flex,
  Heading,
  Image,
  Rating,
  SelectField,
  StepperField,
  SwitchField,
  Text,
  View,
} from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { PAINTINGS } from './paintings';

export default function Home() {
  const [currentPainting, setCurrentPainting] = React.useState(PAINTINGS[0]);
  const [image, setImage] = React.useState(PAINTINGS[0].src);
  const [frame, setFrame] = React.useState(true);
  const [quantity, setQuantity] = React.useState(1);
  const [size, setSize] = React.useState('');
  const [error, setError] = React.useState(false);

  const handleAddToCart = () => {
    if (size === '') {
      setError(true);
      return;
    }
    alert(
      `Added to cart!\n${quantity} ${size} "${currentPainting.title}" by ${
        currentPainting.artist
      } with ${frame ? 'a' : 'no'} frame`
    );
  };

  return (
    <View width="100%" maxWidth="50rem" padding={{ base: 0, large: '2rem' }}>
      <Card variation="outlined">
        <Flex
          direction={{ base: 'column', large: 'row' }}
          justifyContent="space-evenly"
        >
          <Flex direction="column" gap="5rem" alignItems="center">
            <View width="15rem" height="19rem">
              <Image
                src={image}
                alt={`${currentPainting.title} abstract painting`}
                width="100%"
                height="21rem"
                border={frame ? '3px solid black' : ''}
              />
            </View>
            <Collection
              type="grid"
              items={PAINTINGS}
              templateColumns="1fr 1fr 1fr 1fr"
              templateRows="1fr 1fr"
              width="14rem"
            >
              {(item, index) => (
                <Flex
                  width="100%"
                  onMouseOver={() => setImage(item.src)}
                  onMouseLeave={() => setImage(currentPainting.src)}
                  key={index}
                  justifyContent="center"
                >
                  <Image
                    src={item.src}
                    alt={`${item.title} abstract painting`}
                    width="2rem"
                    height="2.5rem"
                    onClick={() => setCurrentPainting(item)}
                    borderRadius="5px"
                    padding="3px"
                    marginBottom="1rem"
                    style={{
                      cursor: 'pointer',
                      ...(currentPainting.src === item.src && {
                        border: '1px solid #e77600',
                        boxShadow: 'rgba(0, 0, 0, 0.35) 0px 3px 8px',
                      }),
                    }}
                  />
                </Flex>
              )}
            </Collection>
          </Flex>
          <Flex direction="column" justifyContent="space-between">
            <Flex direction="column" gap="0.7rem">
              <Flex justifyContent="space-between" alignItems="center">
                <Heading level={3}>{currentPainting.title}</Heading>
                <Flex height="1.8rem">
                  {currentPainting.bestSeller ? (
                    <Badge variation="success">Bestseller</Badge>
                  ) : null}
                  {currentPainting.isNew ? (
                    <Badge variation="info">New</Badge>
                  ) : null}
                  {currentPainting.limitedSupply ? (
                    <Badge variation="warning">Limited supply</Badge>
                  ) : null}
                </Flex>
              </Flex>
              <Text fontWeight="bold">{currentPainting.artist}</Text>
              <Flex
                direction={{ base: 'column', large: 'row' }}
                alignItems="baseline"
              >
                <Rating
                  value={currentPainting.avgRating}
                  fillColor="#f4a41d"
                ></Rating>
                <Text fontSize="small" fontWeight="lighter">
                  {currentPainting.reviews} reviews
                </Text>
              </Flex>
              <Divider />
              <Flex alignItems="baseline">
                <Text fontSize="medium" fontWeight="bold">
                  Price:
                </Text>
                <Text fontSize="large" color="#B12704" fontWeight="bold">
                  {currentPainting.price}
                </Text>
              </Flex>
              <Text fontSize="small" paddingBottom="1rem">
                {currentPainting.description}
              </Text>
              {currentPainting.readyForPickup ? (
                <Text>
                  <Text variation="success" as="span">
                    Ready within 2 hours
                  </Text>{' '}
                  for pickup inside the store
                </Text>
              ) : null}
              <SwitchField
                label={frame ? 'Frame' : 'No frame'}
                labelPosition="end"
                isChecked={frame}
                onChange={(e) => {
                  setFrame(e.target.checked);
                }}
                isDisabled={!currentPainting.inStock}
              />
              <SelectField
                label="Size"
                labelHidden
                variation="quiet"
                placeholder="Select your size"
                value={size}
                onChange={(e) => {
                  e.target.value !== '' && setError(false);
                  setSize(e.target.value);
                }}
                hasError={error}
                errorMessage="Please select a size."
                isDisabled={!currentPainting.inStock}
              >
                <option value="Small" label='Small (12x16")' />
                <option value="Medium" label='Medium (18x24")' />
                <option value="Large" label='Large (24x36")' />
                <option value="X-Large" label='X-Large (30x40")' disabled />
              </SelectField>
              {!currentPainting.inStock ? (
                <Alert variation="error">Out of stock!</Alert>
              ) : null}
            </Flex>
            <Flex
              justifyContent="space-between"
              direction={{ base: 'column', large: 'row' }}
            >
              <Flex alignItems="center" gap="5px">
                <Text>Qty:</Text>
                <StepperField
                  label="Quantity"
                  value={quantity}
                  onStepChange={setQuantity}
                  min={0}
                  max={10}
                  step={1}
                  labelHidden
                  width="10rem"
                  isDisabled={!currentPainting.inStock}
                />
              </Flex>
              <Button
                variation="primary"
                onClick={handleAddToCart}
                disabled={!currentPainting.inStock || !quantity}
              >
                Add to Cart
              </Button>
            </Flex>
          </Flex>
        </Flex>
      </Card>
    </View>
  );
}
Hallway abstract painting
Hallway abstract painting
Fire and Ice abstract painting
Orange, pink, yellow abstract painting
Melted Purple abstract painting
Experimental abstract painting
Rainbow abstract painting
Fearless Hue abstract painting
Liquid abstract painting

Hallway

Bestseller

Efe Kurnaz

4.8 out of 5 rating

445 reviews


Price:

$899.99

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Ac feugiat sed lectus vestibulum.

Ready within 2 hours for pickup inside the store

Qty:

Next.js 13.4+ introduced app directory with the usage of Server Components. In this guide we placed "use client" at the top of page.jsx to mark it as a Client Component.

If you’d like to play around with a more comprehensive demo of the Amplify UI library, create a separate file under the app folder named paintings.js, and copy/paste this code into it:

export const PAINTINGS = [
  {
    title: 'Hallway',
    artist: 'Efe Kurnaz',
    src: 'https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Ac feugiat sed lectus vestibulum.',
    price: '$899.99',
    avgRating: 4.8,
    reviews: 445,
    inStock: true,
    readyForPickup: true,
    bestSeller: true,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Fire and Ice',
    artist: 'Pawel Czerwinski',
    src: 'https://images.unsplash.com/photo-1604871000636-074fa5117945?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, ubique patrioque at qui, modo hinc ne duo, ad consul animal volumus est. Ea quo etiam deleniti, amet singulis in sed. Omnesque lobortis vis ex. Wisi latine splendide vis ei, libris commodo no has.',
    price: '$699.99',
    avgRating: 4.1,
    reviews: 222,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Orange, pink, yellow',
    artist: 'Kseniya Lapteva',
    src: 'https://images.unsplash.com/photo-1629196914375-f7e48f477b6d?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1306',
    description:
      'Lorem ipsum dolor sit amet, cu porro vivendum ius. Ad mei sint homero, cum an soluta epicurei. At pri minimum corrumpit. Minim percipitur eu mei, erant habemus deserunt qui et.',
    price: '$139.99',
    avgRating: 3.5,
    reviews: 142,
    inStock: true,
    readyForPickup: false,
    bestSeller: false,
    isNew: true,
    limitedSupply: false,
  },
  {
    title: 'Melted Purple',
    artist: 'Maria Orlova',
    src: 'https://images.unsplash.com/photo-1549490349-8643362247b5?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, facer nemore ei sea, mea facilis eloquentiam at. Et modus pertinax tincidunt est. Propriae argumentum necessitatibus eos ad.',
    price: '$499.99',
    avgRating: 4.5,
    reviews: 301,
    inStock: true,
    readyForPickup: true,
    bestSeller: true,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Experimental',
    artist: 'Bruno Thethe',
    src: 'https://images.unsplash.com/photo-1550275994-cdc89cd1948f?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, ea probo choro tollit pri, ad pro justo intellegam repudiandae, labores civibus eu quo. Cum latine instructior at, est no odio tibique epicuri.',
    price: '$159.99',
    avgRating: 3.1,
    reviews: 56,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: true,
    limitedSupply: false,
  },
  {
    title: 'Rainbow',
    artist: 'Felix Spiske',
    src: 'https://images.unsplash.com/photo-1543857778-c4a1a3e0b2eb?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1310',
    description:
      'Lorem ipsum dolor sit amet, sea tritani indoctum cu, facilis praesent at qui. Cu cetero veritus vel, et prima erant perfecto vix. Tollit delectus scaevola duo et, inermis sensibus voluptatum cu ius.',
    price: '$799.99',
    avgRating: 4.9,
    reviews: 550,
    inStock: true,
    readyForPickup: false,
    bestSeller: true,
    isNew: false,
    limitedSupply: true,
  },
  {
    title: 'Fearless Hue',
    artist: 'Radienta',
    src: 'https://images.unsplash.com/photo-1579547621113-e4bb2a19bdd6?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=939',
    description:
      'Lorem ipsum dolor sit amet, te labore lucilius pro, te his consul singulis, cu vel unum impedit complectitur. In usu erat dicta doctus, purto aeterno vis te. Facete deterruisset nec id. At omittam antiopam pri.',
    price: '$249.99',
    avgRating: 3.3,
    reviews: 294,
    inStock: true,
    readyForPickup: true,
    bestSeller: false,
    isNew: false,
    limitedSupply: false,
  },
  {
    title: 'Liquid',
    artist: 'Joel Filipe',
    src: 'https://images.unsplash.com/photo-1485163819542-13adeb5e0068?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
    description:
      'Lorem ipsum dolor sit amet, cum cu meliore tacimates, vel no sale maiorum. His cu autem placerat. Homero urbanitas vituperata ad sit, ex mel convenire elaboraret.',
    price: '$549.99',
    avgRating: 4.5,
    reviews: 440,
    inStock: false,
    readyForPickup: false,
    bestSeller: false,
    isNew: false,
    limitedSupply: true,
  },
];

Then, replace the contents of app/page.jsx with the code below, restart the app, and experience all the beautiful components in action!

'use client';

import * as React from 'react';
import {
  Alert,
  Badge,
  Button,
  Card,
  Collection,
  Divider,
  Flex,
  Heading,
  Image,
  Rating,
  SelectField,
  StepperField,
  SwitchField,
  Text,
  View,
} from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { PAINTINGS } from './paintings';

export default function Home() {
  const [currentPainting, setCurrentPainting] = React.useState(PAINTINGS[0]);
  const [image, setImage] = React.useState(PAINTINGS[0].src);
  const [frame, setFrame] = React.useState(true);
  const [quantity, setQuantity] = React.useState(1);
  const [size, setSize] = React.useState('');
  const [error, setError] = React.useState(false);

  const handleAddToCart = () => {
    if (size === '') {
      setError(true);
      return;
    }
    alert(
      `Added to cart!\n${quantity} ${size} "${currentPainting.title}" by ${
        currentPainting.artist
      } with ${frame ? 'a' : 'no'} frame`
    );
  };

  return (
    <View width="100%" maxWidth="50rem" padding={{ base: 0, large: '2rem' }}>
      <Card variation="outlined">
        <Flex
          direction={{ base: 'column', large: 'row' }}
          justifyContent="space-evenly"
        >
          <Flex direction="column" gap="5rem" alignItems="center">
            <View width="15rem" height="19rem">
              <Image
                src={image}
                alt={`${currentPainting.title} abstract painting`}
                width="100%"
                height="21rem"
                border={frame ? '3px solid black' : ''}
              />
            </View>
            <Collection
              type="grid"
              items={PAINTINGS}
              templateColumns="1fr 1fr 1fr 1fr"
              templateRows="1fr 1fr"
              width="14rem"
            >
              {(item, index) => (
                <Flex
                  width="100%"
                  onMouseOver={() => setImage(item.src)}
                  onMouseLeave={() => setImage(currentPainting.src)}
                  key={index}
                  justifyContent="center"
                >
                  <Image
                    src={item.src}
                    alt={`${item.title} abstract painting`}
                    width="2rem"
                    height="2.5rem"
                    onClick={() => setCurrentPainting(item)}
                    borderRadius="5px"
                    padding="3px"
                    marginBottom="1rem"
                    style={{
                      cursor: 'pointer',
                      ...(currentPainting.src === item.src && {
                        border: '1px solid #e77600',
                        boxShadow: 'rgba(0, 0, 0, 0.35) 0px 3px 8px',
                      }),
                    }}
                  />
                </Flex>
              )}
            </Collection>
          </Flex>
          <Flex direction="column" justifyContent="space-between">
            <Flex direction="column" gap="0.7rem">
              <Flex justifyContent="space-between" alignItems="center">
                <Heading level={3}>{currentPainting.title}</Heading>
                <Flex height="1.8rem">
                  {currentPainting.bestSeller ? (
                    <Badge variation="success">Bestseller</Badge>
                  ) : null}
                  {currentPainting.isNew ? (
                    <Badge variation="info">New</Badge>
                  ) : null}
                  {currentPainting.limitedSupply ? (
                    <Badge variation="warning">Limited supply</Badge>
                  ) : null}
                </Flex>
              </Flex>
              <Text fontWeight="bold">{currentPainting.artist}</Text>
              <Flex
                direction={{ base: 'column', large: 'row' }}
                alignItems="baseline"
              >
                <Rating
                  value={currentPainting.avgRating}
                  fillColor="#f4a41d"
                ></Rating>
                <Text fontSize="small" fontWeight="lighter">
                  {currentPainting.reviews} reviews
                </Text>
              </Flex>
              <Divider />
              <Flex alignItems="baseline">
                <Text fontSize="medium" fontWeight="bold">
                  Price:
                </Text>
                <Text fontSize="large" color="#B12704" fontWeight="bold">
                  {currentPainting.price}
                </Text>
              </Flex>
              <Text fontSize="small" paddingBottom="1rem">
                {currentPainting.description}
              </Text>
              {currentPainting.readyForPickup ? (
                <Text>
                  <Text variation="success" as="span">
                    Ready within 2 hours
                  </Text>{' '}
                  for pickup inside the store
                </Text>
              ) : null}
              <SwitchField
                label={frame ? 'Frame' : 'No frame'}
                labelPosition="end"
                isChecked={frame}
                onChange={(e) => {
                  setFrame(e.target.checked);
                }}
                isDisabled={!currentPainting.inStock}
              />
              <SelectField
                label="Size"
                labelHidden
                variation="quiet"
                placeholder="Select your size"
                value={size}
                onChange={(e) => {
                  e.target.value !== '' && setError(false);
                  setSize(e.target.value);
                }}
                hasError={error}
                errorMessage="Please select a size."
                isDisabled={!currentPainting.inStock}
              >
                <option value="Small" label='Small (12x16")' />
                <option value="Medium" label='Medium (18x24")' />
                <option value="Large" label='Large (24x36")' />
                <option value="X-Large" label='X-Large (30x40")' disabled />
              </SelectField>
              {!currentPainting.inStock ? (
                <Alert variation="error">Out of stock!</Alert>
              ) : null}
            </Flex>
            <Flex
              justifyContent="space-between"
              direction={{ base: 'column', large: 'row' }}
            >
              <Flex alignItems="center" gap="5px">
                <Text>Qty:</Text>
                <StepperField
                  label="Quantity"
                  value={quantity}
                  onStepChange={setQuantity}
                  min={0}
                  max={10}
                  step={1}
                  labelHidden
                  width="10rem"
                  isDisabled={!currentPainting.inStock}
                />
              </Flex>
              <Button
                variation="primary"
                onClick={handleAddToCart}
                disabled={!currentPainting.inStock || !quantity}
              >
                Add to Cart
              </Button>
            </Flex>
          </Flex>
        </Flex>
      </Card>
    </View>
  );
}
Hallway abstract painting
Hallway abstract painting
Fire and Ice abstract painting
Orange, pink, yellow abstract painting
Melted Purple abstract painting
Experimental abstract painting
Rainbow abstract painting
Fearless Hue abstract painting
Liquid abstract painting

Hallway

Bestseller

Efe Kurnaz

4.8 out of 5 rating

445 reviews


Price:

$899.99

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Ac feugiat sed lectus vestibulum.

Ready within 2 hours for pickup inside the store

Qty:

Amplify open source software, documentation and community are supported by Amazon Web Services.

© 2024 Amazon Web Services, Inc. and its affiliates. All rights reserved. View the site terms and privacy policy.

Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.