TypeScript Tutorial: Building a Simple Web Application for an Image Resizer

In the digital age, images are everywhere. From social media to e-commerce, websites rely heavily on visual content. However, large image files can significantly impact website performance, leading to slow loading times and a poor user experience. This is where image resizing comes in. By optimizing images for the web, you can improve website speed and enhance overall user satisfaction. This tutorial will guide you through building a simple web application using TypeScript that allows users to upload images and resize them effortlessly.

Why Build an Image Resizer?

As a senior software engineer, I often encounter the need to optimize images. It’s a common requirement in various projects. Here’s why building an image resizer is a valuable skill:

  • Performance: Resizing images reduces file sizes, leading to faster loading times.
  • User Experience: Faster websites result in a better user experience, encouraging users to stay longer and explore more content.
  • SEO: Website speed is a ranking factor for search engines. Optimized images can improve your website’s SEO.
  • Practicality: Image resizing is a common task, and having a tool to automate it can save time and effort.

Setting Up the Project

Let’s get started by setting up our project. We’ll use TypeScript, HTML, and JavaScript. We will use a basic HTML structure and JavaScript to handle the image upload and resizing logic. We will be using the canvas API to do the resizing.

Prerequisites

  • Node.js and npm (or yarn) installed on your system.
  • A basic understanding of HTML, CSS, and JavaScript.
  • A code editor (e.g., VS Code, Sublime Text).

Project Initialization

First, create a new directory for your project and navigate into it using your terminal:

mkdir image-resizer-app
cd image-resizer-app

Next, initialize a new npm project:

npm init -y

This will create a package.json file in your project directory.

Installing TypeScript

Now, let’s install TypeScript and initialize a TypeScript configuration file:

npm install typescript --save-dev
npx tsc --init

This command installs TypeScript as a development dependency and generates a tsconfig.json file. This file configures the TypeScript compiler. Open the tsconfig.json file and modify the following settings (if they are not already set):


{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

These settings configure TypeScript to compile to ES5 JavaScript, use the CommonJS module system, output the compiled files to a dist directory, enable ES module interop, enforce consistent casing, enable strict type checking, and skip library checks. The include array specifies that all files in the src directory should be compiled.

Creating the Project Structure

Create the following file structure in your project directory:


image-resizer-app/
├── src/
│   ├── index.ts
├── dist/
├── index.html
├── package.json
├── tsconfig.json

The src directory will hold our TypeScript files, dist will contain the compiled JavaScript, and index.html will be our main HTML file.

Building the HTML Structure

Create an index.html file in the root directory and add the following HTML structure:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Resizer</title>
</head>
<body>
    <input type="file" id="imageUpload" accept="image/*">
    <br>
    <label for="width">Width:</label>
    <input type="number" id="width" value="200">
    <label for="height">Height:</label>
    <input type="number" id="height" value="200">
    <br>
    <button id="resizeButton">Resize</button>
    <br>
    <canvas id="imageCanvas"></canvas>
    <br>
    <a id="downloadLink" style="display:none;">Download</a>
    <script src="dist/index.js"></script>
</body>
</html>

This HTML provides an input for uploading an image, input fields for specifying the desired width and height, a button to trigger the resizing, a canvas element to display the resized image, and a download link.

Writing the TypeScript Code

Now, let’s write the TypeScript code to handle the image upload, resizing, and download functionality. Create an index.ts file inside the src directory and add the following code:


const imageUpload = document.getElementById('imageUpload') as HTMLInputElement;
const widthInput = document.getElementById('width') as HTMLInputElement;
const heightInput = document.getElementById('height') as HTMLInputElement;
const resizeButton = document.getElementById('resizeButton') as HTMLButtonElement;
const imageCanvas = document.getElementById('imageCanvas') as HTMLCanvasElement;
const downloadLink = document.getElementById('downloadLink') as HTMLAnchorElement;


resizeButton?.addEventListener('click', () => {
  const file = imageUpload.files?.[0];
  if (!file) {
    alert('Please select an image.');
    return;
  }

  const width = parseInt(widthInput.value, 10);
  const height = parseInt(heightInput.value, 10);

  if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) {
    alert('Please enter valid width and height values.');
    return;
  }

  const reader = new FileReader();
  reader.onload = (e: ProgressEvent<FileReader>) => {
    if (e.target?.result) {
      const img = new Image();
      img.onload = () => {
        resizeImage(img, width, height);
      };
      img.src = e.target.result as string;
    }
  };
  reader.readAsDataURL(file);
});


function resizeImage(img: HTMLImageElement, width: number, height: number): void {
  const canvas = imageCanvas;
  const ctx = canvas.getContext('2d');

  if (!ctx) {
    console.error('Could not get 2D context');
    return;
  }

  canvas.width = width;
  canvas.height = height;
  ctx.drawImage(img, 0, 0, width, height);

  const dataURL = canvas.toDataURL('image/jpeg');
  downloadLink.href = dataURL;
  downloadLink.download = 'resized-image.jpg';
  downloadLink.style.display = 'block';
}

Let’s break down this code:

  • Get Elements: We retrieve references to the HTML elements using their IDs.
  • Event Listener: An event listener is attached to the “Resize” button. When clicked, it triggers the image resizing process.
  • File Handling: It retrieves the selected file from the input and checks if a file has been selected.
  • Input Validation: It parses the width and height inputs, validates them to ensure they are valid numbers, and are greater than zero.
  • FileReader: A FileReader is used to read the image file as a data URL.
  • Image Creation: An Image object is created, and its onload event is used to trigger the resizing once the image is loaded.
  • resizeImage Function:
    • Gets the 2D rendering context of the canvas.
    • Sets the canvas width and height to the specified dimensions.
    • Draws the image onto the canvas, scaling it to fit the new dimensions.
    • Converts the canvas content to a data URL (JPEG format).
    • Sets the download link’s href to the data URL and sets the download attribute to specify the filename.
    • Displays the download link.

Compiling and Running the Application

Now, compile the TypeScript code using the TypeScript compiler:

tsc

This command will generate a dist/index.js file containing the compiled JavaScript. Now, open index.html in your web browser. You should see the image upload input, width and height inputs, the resize button, and the canvas element.

Upload an image, enter the desired width and height, click the “Resize” button. The resized image will appear in the canvas, and a download link will appear below the canvas. Click the download link to save the resized image to your computer.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid or fix them:

  • Incorrect File Type: Make sure the user selects an image file. The accept="image/*" attribute in the HTML input restricts the file selection to image types.
  • Invalid Input Values: Always validate the width and height inputs to ensure they are valid numbers and are greater than zero. The example code includes input validation.
  • Canvas Context Error: Ensure that the canvas 2D context is properly retrieved. The code includes a check to verify the context.
  • CORS Issues: If you are loading images from a different domain, you might encounter CORS (Cross-Origin Resource Sharing) issues. To resolve this, ensure that the server serving the images allows cross-origin requests. Alternatively, you can download the image and resize it locally.
  • Asynchronous Operations: Be mindful of asynchronous operations (e.g., reading the file, loading the image) and ensure that the code waits for these operations to complete before proceeding. The code uses the onload event of the Image object to handle this.

Enhancements and Further Development

This is a basic image resizer. Here are some ideas for enhancements:

  • Add more image formats: Support additional image formats (e.g., PNG, GIF) by specifying the correct MIME type in the toDataURL() method.
  • Implement different resizing algorithms: Explore different resizing algorithms (e.g., bilinear interpolation, bicubic interpolation) for improved image quality.
  • Add image preview: Display a preview of the uploaded image before resizing.
  • Add error handling: Implement more robust error handling to handle different scenarios (e.g., file upload errors, invalid image formats).
  • Add a progress indicator: Show a progress indicator while the image is being resized.
  • Implement server-side resizing: For larger images, consider performing the resizing on the server to avoid performance issues in the client-side.

Key Takeaways

  • Image resizing is crucial for web performance and user experience.
  • TypeScript provides strong typing and code organization benefits.
  • The HTML Canvas API is a powerful tool for image manipulation.
  • Proper validation and error handling are essential for a robust application.

FAQ

Q: What are the benefits of using TypeScript for this project?

A: TypeScript adds static typing to JavaScript, which helps catch errors during development, improves code readability, and makes it easier to maintain and refactor the code.

Q: Can I use this image resizer for commercial purposes?

A: Yes, you can use this code for commercial purposes, but it’s always a good idea to review the licensing of any third-party libraries or components you might use.

Q: How can I handle larger images to avoid performance issues?

A: For larger images, consider server-side resizing or using web workers to perform the resizing in a separate thread, preventing the UI from freezing.

Q: What is the best way to handle different image formats?

A: When saving the resized image, use the appropriate MIME type in the toDataURL() method (e.g., 'image/png' for PNG, 'image/gif' for GIF).

Conclusion

This tutorial has provided a practical introduction to building a simple image resizer using TypeScript. You’ve learned how to set up a project, handle image uploads, resize images using the canvas API, and download the resized images. By applying these concepts, you can optimize images for your web applications, improve performance, and enhance the user experience. Remember that this is just the beginning; there’s always room for improvement and further development. Explore the enhancements mentioned earlier, experiment with different resizing algorithms, and continue to learn and grow your skills as a software engineer. The knowledge gained from this project can be applied in various web development scenarios, making you a more versatile and effective developer. Embrace the power of image optimization and make your websites faster and more user-friendly.