> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Try before you buy sample project


This guide demonstrates how to build an e-commerce style application that allows potential customers to upload a photo of their room to visualise how different colored paints would look on their walls. 

![Coral sunset walls](https://cloudinary-res.cloudinary.com/image/upload/bo_2px_solid_gray/f_auto/q_auto/docs/try_before_you_buy_app.png "thumb: c_scale,w_800/dpr_2.0, width:800, popup:true")

> **TIP**: :title=View the code

You can find the code for this sample project in [GitHub](https://github.com/cloudinary-devs/try-before-buy).

Another dimension to this project is to add functionality to clean up your product environment, removing uploaded images that are no longer required. See [Delete temporary UGC assets](delete_temporary_ugc_assets).

## Overview

The app serves as a demonstration platform for handling user-generated content in an e-commerce context. It implements these main features:

* Client-side uploading using a drag-and-drop interface.
* Limiting of image dimensions to prevent the storage of overly large images in the product environment.
* On-the-fly recoloroing of the walls in the image using generative AI according the user's color selection.
* Delivery of the image in the best format and quality for the user's browser, and resized to fit the design.

### Key Cloudinary features

Learn about how each of these Cloudinary features have been implemented in this app:

* [Upload preset](#upload_preset): the instructions for uploading images
* [Upload API](#upload_api): the method of uploading images
* [Transformations and optimizations](#transformations_and_optimizations): modifying images on the fly

## Try it out

Here's the app in action:

To run the app yourself: 

1. Clone or fork the [GitHub repo](https://github.com/cloudinary-devs/try-before-buy).
1. In **app/config/cloudinary.ts**, replace **MY_CLOUD_NAME** with your Cloudinary product environment cloud name.
    
What's my cloud name?

You can find your **Cloud name** near the top of the [Dashboard](https://console.cloudinary.com/app/home/dashboard) of the Cloudinary Console. [Sign up for free](https://cloudinary.com/users/register_free) if you don't yet have a Cloudinary account.

1. Create an unsigned upload preset called **try-before-buy**. (You can use a different name, but if you do, you also need update the `uploadPreset` value in **cloudinary.ts**.) See instructions on how to [configure your upload preset](#upload_preset_configuration).
1. Run the development server:
   
      ```terminal
      npm i
      ```
      then

      ```terminal
      npm run dev
      ```

      Then open [http://localhost:5173](http://localhost:5173) (or the relevant port) in your browser to see the app running. 

### Upload preset configuration

To configure the upload preset:

1. Log into your [Cloudinary Console](https://console.cloudinary.com). 
1. Navigate to **Settings > Upload > Upload Presets**.
1. Click **Add Upload Preset**. 
1. Configure each of the sections as shown below, then click **Save**:

#### General

{table:class=wide-2ndcol} Parameter | Value | Meaning
--|--|--
 **Upload preset name** | `try-before-buy` | The name of the upload preset. This must match the **uploadPreset** parameter used in the [Upload API](#upload_api) call (set in **cloudinary.ts**).
 **Signing mode** | `Unsigned` | No signature is required for uploading assets using this upload preset.
 **Auto-generate an unguessable public ID value** | `true` | It's best to generate a random value to avoid conflicts if you have many users uploading their images to your product environment.

The rest of the **General** settings can be set as you like.

![Advanced upload preset settings](https://cloudinary-res.cloudinary.com/image/upload/bo_1px_solid_gray/f_auto/q_auto/docs/tbb_general.png "thumb: c_scale,w_600/dpr_2.0, width:600, popup:true")

#### Transform

{table:class=wide-2ndcol} Parameter | Value | Meaning
--|--|--
 **Incoming transformation** | `c_limit,h_3000,w_3000/fl_force_strip` | Limit the dimensions of the image to 3000 by 3000 pixels and strip embedded metadata associated with the image. Although the app displays images at  smaller dimensions, the recolor effect works better on higher resolution images.

![Transform upload preset settings](https://cloudinary-res.cloudinary.com/image/upload/bo_1px_solid_gray/f_auto/q_auto/docs/tbb_transform_tab.png "thumb: c_scale,w_600/dpr_2.0, width:600, popup:true")

#### Manage and Analyze

{table:class=wide-2ndcol} Parameter | Value | Meaning
--|--|--
 **Tags** | `ugc-content` | Tag this asset as UGC content so you know that you can [delete it at the appropriate time](delete_temporary_ugc_assets).

![Manage and analyze upload preset settings](https://cloudinary-res.cloudinary.com/image/upload/bo_1px_solid_gray/f_auto/q_auto/docs/tbb_manage.png "thumb: c_scale,w_600/dpr_2.0, width:600, popup:true")

## Deep dive  

If you want to learn how each of the features have been implemented in detail, expand the following sections:

### Architecture #### Technology stack

The app is built using the following technology:

* React with TypeScript
* Tailwind CSS
* Cloudinary React SDK and Upload API
  
##### Project files

The main project files are as follows:

{table:class=no-borders overview align-bullets} File | Functionality
--|--
[src/config/cloudinary.ts](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/config/cloudinary.ts) | Contains configuration details specific to the app instance
[src/utils/cloudinaryUtils.ts](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/utils/cloudinaryUtils.ts) | Handles image uploads using Cloudinary's Upload API
[src/components/ImageUploader.tsx](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/components/ImageUploader.tsx) | Enables the upload of files to Cloudinary through a drag-and-drop interface
[src/components/ColorSwatch.tsx](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/components/ColorSwatch.tsx) | Renders individual paint color swatches that users can click to preview different colors on their room image
[src/components/ColoredRoomPreview.tsx](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/components/ColoredRoomPreview.tsx) | Displays the uploaded imageReacts to changes to the color swatch selection resulting in recolored walls in the imagesApplies Cloudinary transformations to resize and optimize the delivery of the image
[src/App.tsx](https://github.com/cloudinary-devs/try-before-buy/blob/main/src/App.tsx) | Manages the state that's shared between components, namely the selected color, the public ID of the uploaded image and the uploading stateControls the layout of the pageHandles uploads and changes to the color swatch

### Upload preset The upload preset specifies what happens when a user uploads an image. 

In the case of this app, the upload preset:

* Generates a random value for the public ID of any uploaded image
* Applies an incoming transformation to limit the image's dimensions and strip embedded metadata
* Adds a tag to the image to make it easy to distinguish user-generated assets from other assets in your product environment.

See how to [configure the upload preset](#upload_preset_configuration).

### Upload API The Cloudinary [Upload API](image_upload_api_reference) provides an endpoint that you can use for uploading files to your product environment. 

In this app, a `fetch` request is made to the `image/upload` endpoint with the body containing the file and the upload preset.

cloudinaryUtils.ts

```react
import { CLOUDINARY_CONFIG } from '../config/cloudinary'

// Function to upload an image to Cloudinary
export const uploadImage = async (file: File): Promise<string> => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('upload_preset', CLOUDINARY_CONFIG.uploadPreset);

  try {
    const response = await fetch(`https://api.cloudinary.com/v1_1/${CLOUDINARY_CONFIG.cloudName}/image/upload`, {
      method: 'POST',
      body: formData
    });

    const data = await response.json();
    return data.public_id;
  } catch (error) {
    console.error('Error uploading image:', error);
    throw error;
  }
};
```

### Transformations and optimizations #### Configure the Cloudinary instance

The Cloudinary instance, required for the transformations, is created in **ColoredRoomPreview.tsx**. The cloud name itself is set in the **cloudinary.ts** configuration file.
   

ColoredRoomPreview.tsx

```react
import { Cloudinary } from "@cloudinary/url-gen"
import { CLOUDINARY_CONFIG } from "../config/cloudinary"

  // Initialize Cloudinary with the correct cloud name
  const cld = new Cloudinary({
    cloud: {
      cloudName: CLOUDINARY_CONFIG.cloudName
    }
  });

```

cloudinary.ts

```javascript
export const CLOUDINARY_CONFIG = {
    cloudName: 'MY_CLOUD_NAME',
    uploadPreset: 'try-before-buy'
  } as const
```

> **INFO**: Remember to change `MY_CLOUD_NAME` to your own cloud name.

#### Apply the generative recolor transformation

When the selected color changes, the [generative recolor](generative_ai_transformations#generative_recolor) effect is applied to the uploaded image. The prompt is set to `wall` and the `detectMultiple()` function is used to ensure the color is applied to all walls in the image.

ColoredRoomPreview.tsx

```react
  let myImage = cld.image(imagePublicId);
  
  if (selectedColor) {
    myImage.effect(
      generativeRecolor("wall", selectedColor.hexCode.substring(1)).detectMultiple()
    );
  }
```

#### Apply the resize transformation

After the recolor, the image is resized to fit the display area. The [limit](resizing_and_cropping#c_limit) resize mode is used so that no part of the image is cropped.

> **TIP**: You get better recolor results on the original image, hence resizing after the recolor.

ColoredRoomPreview.tsx

```react
  // Apply size limit after recoloring
  myImage.resize(limitFit().width(736).height(500));
```

#### Apply the optimization transformations

The last transformations applied ensure the image is delivered in the best [format](image_optimization#automatic_format_selection_f_auto) and [quality](image_optimization#automatic_quality_selection_q_auto) for the requesting browser. 

ColoredRoomPreview.tsx

```react
  // Optimize format and quality
  myImage.format('auto').quality('auto');
```

#### Use the AdvancedImage component

The uploaded image is delivered using the React SDK [AdvancedImage component](react_image_transformations#image_transformations_with_react).

For example:

ColoredRoomPreview.tsx

```react
<AdvancedImage cldImg={cldImage} className={`max-w-full max-h-full ${isLoading ? 'opacity-50' : 'opacity-100 transition-opacity duration-300'}`} />
```

The full URL generated for the recolored image is:

![URL of the profile picture](https://res.cloudinary.com/demo/image/upload/e_gen_recolor:prompt_wall;to-color_546e7a;multiple_true/c_limit,h_500,w_736/f_auto/q_auto/docs/bedroom-1872196_1920 "with_image:false, with_code:false")

Original image

Recolored image