Last updated: September 18, 2024

<span aria-hidden="true" id="create-custom-valentines-cards-with-transloadit--uppy"></span>

# Create custom Valentine's cards with Transloadit & Uppy

![Joseph Grabski](/assets/images/teammates/joseph2.png?dpl=dpl_6nhQNS5wkVkPZWuKMzXcWrAHNJL5)

**Joseph Grabski**

Content Lead · Rochester, United Kingdom · Show bio

[](https://x.com/joe%5Fgrabski)[](https://github.com/Missing-Tech)

As Valentine's Day approaches, we want to share a fun project that you can build using Transloadit and Uppy. We'll be using the [/image/resize](/docs/robots/image-resize.md) Robot to create a custom Valentine's Day card generator. Add a name to the card too, and you'll have a personalized card to send to your loved one.

![A cute cartoony banner with several love hearts, love letters and arrows around the text 'Valentine Card Generator'.](/_next/static/immutable/media/opengraph-image.2_7brbyg9i75t.jpg)

<span aria-hidden="true" id="our-assembly-instructions"></span>

## Our Assembly Instructions

Let's first take a look at the Assembly Instructions we'll be using.

```json
{
  "steps": {
    "watermark": {
      "use": ":original",
      "robot": "/image/resize",
      "width": "1921",
      "height": "1921",
      "resize_strategy": "fillcrop",
      "watermark_url": "../../../../../../_assets/images/blog/2023-01-26-valentines-card-generator-1.png",
      "watermark_size": "100%",
      "watermark_position": "center",
      "imagemagick_stack": "{{stacks.imagemagick.recommended_version}}"
    },
    "add_text": {
      "use": "watermark",
      "robot": "/image/resize",
      "text": [
        {
          "text": "Happy Valentines Day!",
          "size": 110,
          "color": "#ffffff",
          "valign": "bottom",
          "y_offset": -90,
          "x_offset": -100
        }
      ],
      "imagemagick_stack": "{{stacks.imagemagick.recommended_version}}"
    }
  }
}

```

Here we need to use two separate [/image/resize](/docs/robots/image-resize.md) Steps. Our first Step adds a decorative frame around our original image, while also cropping and resizing it to fit. The second Step then adds text on top of our watermarked image, which we'll be modifying later with JavaScript. If we did this in the same Step, our image frame would be placed in front of the text – obscuring it in our final result.

<span aria-hidden="true" id="html--css"></span>

## HTML & CSS

Sadly, we have nowhere to show off our new Template. Let's fix that! Copy the below HTML into a new HTML file.

One thing to take note of is our `input` element. We'll be using this in the next section to allow the user to change what name to add to their card.

```html
<html>
  <head>
    <title>Transloadit Valentine</title>
    <link href="https://releases.transloadit.com/uppy/v3.3.1/uppy.min.css" rel="stylesheet" />
  </head>

  <style></style>

  <body>
    <h1>Transloadit Valentine</h1>
    <p>Upload a picture of your Valentine and we'll make it a Valentine's Day card!</p>

    <div class="input-container">
      <label for="name">Name:</label>
      <input id="name" name="name" maxlength="12" />
    </div>

    <div id="drag-drop-area"></div>

    <img id="result" alt="Your result image" src="" />
  </body>
</html>

```

Now, let's add some nice styling to our page. Inside of the `style` tag, add the following CSS.

```css
:root {
  --primary: #7f626a;
  --secondary: #f8edeb;
  --accent: #ff4d6d;
  --accent-highlight: #f9ccd2;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: var(--secondary);
  color: var(--primary);
  font-family: sans-serif;
}

h1,
p {
  margin: 0 0 0.5rem;
}

#result {
  border-radius: 10px;
  max-width: 40vw;
  border: none;
  contain: content;
  aspect-ratio: 1/1;
  margin: 2rem;
}

.input-container {
  margin: 1rem;
  color: var(--primary);
}

input {
  border: none;
  border-bottom: 2px var(--primary) dashed;
  background-color: transparent;
  text-align: start;
  font: inherit;
  color: var(--accent);
}

input:focus {
  outline: none;
}

.uppy-Dashboard {
  max-width: 90vw;
}

.uppy-Dashboard-innerWrap {
  background-color: var(--secondary);
  border: var(--primary) 2px dashed;
}

.uppy-Dashboard-AddFiles {
  border: 0 !important;
}

.uppy-Dashboard-poweredBy,
.uppy-Dashboard-poweredByIcon {
  color: var(--primary) !important;
  stroke: var(--primary);
}

.uppy-Dashboard-AddFiles-title,
.uppy-DashboardTab-name {
  color: var(--primary);
}

.uppy-DashboardTab-btn:hover {
  background-color: var(--accent-highlight) !important;
}

.uppy-Dashboard-browse,
.uppy-ProviderIconBg,
.uppy-DashboardContent-back,
.uppy-StatusBar-actionBtn {
  color: var(--accent);
  fill: var(--accent);
}

.uppy-DashboardContent-bar,
.uppy-StatusBar-actions,
.uppy-StatusBar,
.uppy-StatusBar::before {
  border: none !important;
  background-color: transparent !important;
}

.uppy-StatusBar-actionBtn--upload {
  background-color: var(--accent) !important;
  color: var(--secondary) !important;
  border: none;
  border-radius: 5px;
}

```

While it may look intimidating, the majority of our CSS here exists only to restyle our Uppy Dashboard component. Other than that, we're just using a color palette generated from[Coolors⁠](https://coolors.co/).

Here's what our page should now look like:

![Our website so far, without the Uppy Dashboard.](/_next/static/immutable/media/2023-01-20-valentines-scaffold.12g2y7kgnl4m0.png)

<span aria-hidden="true" id="js"></span>

## JS

Now onto the fun part! Inside of a `script` tag, we need to add the following JavaScript to our website.

```javascript
import {
  Uppy,
  Dashboard,
  Transloadit,
  ImageEditor,
} from 'https://releases.transloadit.com/uppy/v3.3.1/uppy.min.mjs'

const baseParams = {
  auth: { key: 'AUTH_KEY' },
  steps: {
    watermark: {
      use: ':original',
      robot: '/image/resize',
      width: '1921',
      height: '1921',
      resize_strategy: 'fillcrop',
      watermark_url: 'https://imgur.com/rF1HytT.png',
      watermark_size: '100%',
      watermark_position: 'center',
      imagemagick_stack: 'v3.0.0',
    },
    add_text: {
      use: 'watermark',
      robot: '/image/resize',
      text: [
        {
          text: 'Happy Valentines Day!',
          size: 110,
          color: '#ffffff',
          valign: 'bottom',
          y_offset: -90,
          x_offset: -100,
        },
      ],
      imagemagick_stack: 'v3.0.0',
    },
  },
}

function updateName(val) {
  if (val) {
    const params = baseParams
    params.steps.add_text.text[0].text = `Happy Valentines ${val}!`
    uppy.getPlugin('Transloadit').setOptions({
      params,
    })
  }
}

const uppy = new Uppy({
  restrictions: {
    maxNumberOfFiles: 1,
    allowedFileTypes: ['image/*'],
  },
})
  .use(Dashboard, {
    inline: true,
    target: '#drag-drop-area',
  })
  .use(ImageEditor, {
    target: Dashboard,
    quality: 0.8,
    cropperOptions: {
      aspectRatio: 1 / 1, // Same aspect ratio as our overlay
    },
  })
  .use(Transloadit, {
    waitForEncoding: true,
    params: baseParams,
  })

uppy.on('complete', (result) => {
  const resultImg = document.getElementById('result')
  resultImg.src = result.transloadit[0].results.add_text[0].ssl_url
})

document.getElementById('name').addEventListener('change', function () {
  updateName(this.value)
})

```

We leverage the Uppy [Dashboard⁠](https://uppy.io/docs/dashboard/),[ImageEditor⁠](https://uppy.io/docs/dashboard/) and [Transloadit⁠](https://uppy.io/docs/transloadit/)components here, and luckily we can mostly use the default settings. Notably however, we set the aspect ratio of the ImageEditor crop tool to the same as our overlay image. In our case, it's 1:1.

We also add an event listener to update the text that we add to the image whenever the value in our`input` element changes.

<span aria-hidden="true" id="wrapping-up"></span>

## Wrapping up

If you want to tinker around with the website without having to implement it yourself – try out the CodePen below!

See the Pen [Untitled](https://codepen.io/missing-tech/pen/abjEJeK) bymissing-tech ([@missing-tech](https://codepen.io/missing-tech)) on [CodePen](https://codepen.io).

That's all from us for now. Happy Valentine's Day, and make sure to send your special someone a beautiful, personalized card. And if you managed to take the tools used in this blog post even further, we would love to hear about it on [Twitter⁠](https://twitter.com/transloadit)!

[#image-resize-robot](/blog/tags/image-resize-robot.md)[#assembly](/blog/tags/assembly.md)[#walkthrough](/blog/tags/walkthrough.md)

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
