QR Code Generator with HTML, CSS & JavaScript

20 DAYS 20 PROJECT CHALLENGE

Day #12

Project Overview

QR Code Generator is a beginner-friendly project built using HTML, CSS, and JavaScript. It allows users to enter text or a URL and generate a QR code that can be scanned using a mobile device or QR code scanner. It sends the entered information to a QR code generation service and displays the generated QR code on the webpage. Users can generate a new QR code whenever they change the text or URL. The project also provides an easy way to download the generated QR code as an image.

This project is useful for beginners who want to practice working with user input, APIs, Fetch API, asynchronous JavaScript, DOM manipulation, and dynamic image updates.

Key Features

  • Text and URL Input: Users can enter normal text, website URLs, contact information, or other supported content in order to create a QR code. The entered content will become the data behind the QR code.
  • Instant QR Code Generation: The application generates the QR code according to the user’s input and displays it on the webpage. The codes can be generated right away by changing the input value.
  • API-Based Generation: It uses an external QR code generation service to create the QR image. This illustrates how the use of JavaScript can allow for communication with external web services.
  • Dynamic QR Code Display: JavaScript updates the QR code image dynamically after the generation request is completed. In this way, the user can see the generated result on the webpage.
  • Input Validation: The application checks if the user entered any text before generating the QR code. This enhances the user experience by removing unnecessary requests.
  • Download QR Code: The user can download the QR code image and save it on their device. This enables the user to use the QR code outside of the application.
  • Generate New Codes: The user can enter different text or URLs and generate multiple QR codes. This allows for fast creation of multiple QR codes without having to refresh the page.
  • Clean and Simple Interface: The application uses a simple interface that only includes an input field, action button, and QR code display area. This enhances the overall user experience by focusing on the necessary elements.
  • Responsive Design: The QR Code Generator web application has a responsive design that allows it to be used on different screen sizes. This includes desktops, tablets, and mobile devices.

What You'll Learn

  • Understand how to collect user input using HTML form elements.
  • Learn how to validate user input with JavaScript.
  • Practice working with external APIs.
  • Understand how fetch() can communicate with a web service.
  • Learn how async and await handle asynchronous operations.
  • Practice creating URLs with query parameters.
  • Understand how JavaScript can update image elements dynamically.
  • Learn how to display loading and error messages.
  • Understand how to create downloadable files or images.
  • Learn how the DOM can be updated based on user input.
  • Practice building a responsive interface using CSS.

HTML Code

HTML creates the structure of the QR Code Generator. The application is placed inside the main container, which holds the project heading, input area, action button, status message, and QR code display section. Also the text input allows users to enter the information that should be converted into a QR code. This can include a website URL, normal text, or other supported content. The Generate button starts the QR code generation process when clicked.

The QR code display area contains an image element where the generated QR code is shown. JavaScript updates the image source after receiving the generated QR code. A Download button can also be used to save the generated QR code image. the script.js file is linked at the bottom of the <body>, allowing JavaScript to access the HTML elements, handle user actions, and dynamically update the QR code on the webpage.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Day 12 — QR Code Generator</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <main class="card" role="main" aria-labelledby="title">
    <header>
      <div class="logo">QR</div>
      <div>
        <h1 id="title">Day 12: QR Code Generator</h1>
        <p class="lead">Generate and download QR codes from any text using the QRServer API and <code>fetch()</code>.</p>
      </div>
    </header>

    <section class="panel">
      <label class="field">
        <span class="label">Text or URL</span>
        <textarea id="inputText" placeholder="Enter a URL or text to convert to QR code" rows="3"></textarea>
      </label>

      <div class="row">
        <label class="field small">
          <span class="label">Size (px)</span>
          <input id="size" type="number" min="100" max="2000" value="300" />
        </label>

        <label class="field small">
          <span class="label">Margin (0-10)</span>
          <input id="margin" type="number" min="0" max="10" value="2" />
        </label>

        <label class="field small">
          <span class="label">Error correction</span>
          <select id="ecc">
            <option value="L">L (7%)</option>
            <option value="M" selected>M (15%)</option>
            <option value="Q">Q (25%)</option>
            <option value="H">H (30%)</option>
          </select>
        </label>
      </div>

      <div class="actions">
        <button id="generateBtn" class="btn">Generate QR</button>
        <button id="openBtn" class="btn secondary" disabled>Open in new tab</button>
        <button id="downloadBtn" class="btn secondary" disabled>Download PNG</button>
      </div>

      <div id="status" class="muted" aria-live="polite">Enter text and click <strong>Generate QR</strong>.</div>

      <div class="preview-wrap">
        <div id="preview" class="preview" aria-hidden="true">
          <!-- generated QR image will be inserted here -->
        </div>
      </div>

      <details style="margin-top:12px">
        <summary>How it works (short)</summary>
        <p class="small">The script builds a query URL (e.g. <code>https://api.qrserver.com/v1/create-qr-code/?size=300x300&amp;data=...&amp;ecc=M&amp;margin=2</code>), calls it with <code>fetch()</code> and receives the PNG image as a blob. We create an object URL and display it in an &lt;img&gt; tag and allow the user to download or open the image in a new tab.</p>
      </details>
    </section>
  </main>

  <script src="script.js"></script>
</body>
</html>

CSS Code

CSS controls the appearance and layout of the QR Code Generator. The main application container is styled with spacing, padding, rounded corners, borders, and a shadow to create a clean card-like interface. The input field and Generate button are styled to make the QR code creation process simple and easy to use. Focus and hover effects can provide visual feedback when users interact with the controls.

The QR code display section controls the size and alignment of the generated image. Flexbox can be used to center the content and keep the layout organized. The generated QR image is given a suitable maximum width so that it remains clearly visible without becoming too large. Responsive CSS rules adjust the spacing and layout for smaller screens, ensuring that the QR Code Generator remains usable on mobile devices.

:root {
  --bg: #071026;
  --card: #0b1220;
  --accent: #7c3aed;
  --muted: #9aa4b2;
  --white: #e6eef6;
  font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, Arial;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: #002252;
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
}

.card {
  width: min(820px, 96%);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.01));
  border-radius: 12px;
  padding: 18px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.03);
}

header {
  display: flex;
  gap: 12px;
  align-items: center;
}

.logo {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--accent), #22c1c3);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

h1 {
  margin: 0;
  font-size: 18px;
}

.lead {
  margin: 4px 0 12px;
  color: var(--muted);
  font-size: 14px;
}

.panel {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.field {
  display: flex;
  flex-direction: column;
}

.field .label {
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 6px;
}

textarea#inputText {
  min-height: 64px;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.04);
  background: transparent;
  color: inherit;
  outline: none;
  resize: vertical;
}

input[type=number],
select {
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.04);
  background: transparent;
  color: inherit;
  width: 100%;
}

.row {
  display: flex;
  gap: 12px;
  align-items: end;
}

.row .small {
  flex: 1;
}

.actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.btn {
  padding: 10px 14px;
  border-radius: 10px;
  border: 0;
  background: linear-gradient(90deg, var(--accent), #22c1c3);
  color: white;
  font-weight: 600;
  cursor: pointer
}

.btn.secondary {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.06);
  color: var(--muted)
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed
}

.muted {
  color: var(--muted);
  font-size: 13px
}

.preview-wrap {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}

.preview {
  width: 320px;
  height: 320px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.01), rgba(255, 255, 255, 0.005));
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.03);
}

.preview img {
  max-width: 100%;
  max-height: 100%;
  display: block;
  border-radius: 6px;
}

.small {
  font-size: 13px;
  color: var(--muted)
}

Javascript Code

JavaScript handles the main functionality of the QR Code Generator. First, it selects the required HTML elements, including the text input, Generate button, QR code image, and status or download controls. When the user clicks the Generate button, JavaScript reads the entered text and checks whether a valid value has been provided. If the input is empty, the application can display a message asking the user to enter some content.

After receiving valid input, the code creates a request URL for the QR code generation service. The user’s text is encoded correctly before being added to the request URL so that spaces and special characters can be handled safely and the generated QR code is then displayed by updating the src property of the image element. If the application uses fetch(), asynchronous JavaScript can handle the request and update the interface when the response is ready.

The JavaScript code also handles loading states and possible errors. Finally, the download functionality allows users to save the generated QR code image to their device. This project provides a practical introduction to working with APIs and dynamically updating webpage content using JavaScript.

// Day 12 — QR Code Generator using QRServer API and fetch()
// No API key required for QRServer. Example endpoint:
// https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=Hello&ecc=M&margin=2

// DOM
const inputText = document.getElementById('inputText');
const sizeInput = document.getElementById('size');
const marginInput = document.getElementById('margin');
const eccSelect = document.getElementById('ecc');

const generateBtn = document.getElementById('generateBtn');
const downloadBtn = document.getElementById('downloadBtn');
const openBtn = document.getElementById('openBtn');

const statusEl = document.getElementById('status');
const previewEl = document.getElementById('preview');

let currentObjectUrl = null; // track created object URL so we can revoke it

// Utility: build QRServer URL
function buildQrUrl({ data, size = 300, ecc = 'M', margin = 2 }) {
  const url = new URL('https://api.qrserver.com/v1/create-qr-code/');
  const px = Number(size) || 300;
  url.searchParams.set('size', `${px}x${px}`);
  url.searchParams.set('data', data || '');
  url.searchParams.set('ecc', ecc || 'M');
  url.searchParams.set('margin', String(margin || 0));
  // Optionally set format param if supported: url.searchParams.set('format','png');
  return url.toString();
}

// Show message
function setStatus(msg, isError = false) {
  statusEl.textContent = msg;
  statusEl.style.color = isError ? '#ffbaba' : '';
}

// Clean preview object URL
function revokeCurrentUrl() {
  if (currentObjectUrl) {
    URL.revokeObjectURL(currentObjectUrl);
    currentObjectUrl = null;
  }
  previewEl.innerHTML = '';
  downloadBtn.disabled = true;
  openBtn.disabled = true;
}

// Fetch QR image as blob and show preview
async function generateQr() {
  const data = inputText.value.trim();
  if (!data) {
    setStatus('Please enter text or URL to generate a QR code.', true);
    return;
  }

  const size = Math.max(100, Math.min(2000, Number(sizeInput.value) || 300));
  const margin = Math.max(0, Math.min(10, Number(marginInput.value) || 2));
  const ecc = eccSelect.value || 'M';

  const url = buildQrUrl({ data, size, ecc, margin });

  setStatus('Generating QR…');
  revokeCurrentUrl();

  try {
    // Fetch the image as a blob so we can offer download and open in tab
    const resp = await fetch(url, { cache: 'no-cache' });
    if (!resp.ok) throw new Error(`Network error ${resp.status}`);

    const blob = await resp.blob();
    // Defensive: ensure it is an image
    if (!blob.type.startsWith('image/')) {
      throw new Error('Invalid response (not an image).');
    }

    // Create object URL and show it
    currentObjectUrl = URL.createObjectURL(blob);
    const img = document.createElement('img');
    img.src = currentObjectUrl;
    img.alt = 'Generated QR code';
    img.onload = () => {
      setStatus('QR generated. You can download or open it in a new tab.');
    };
    img.onerror = () => {
      setStatus('Failed to load generated image.', true);
    };

    previewEl.appendChild(img);
    downloadBtn.disabled = false;
    openBtn.disabled = false;
  } catch (err) {
    console.error(err);
    setStatus('Failed to generate QR code. Try again.', true);
  }
}

// Download current image (as .png)
function downloadCurrentImage() {
  if (!currentObjectUrl) return;
  const link = document.createElement('a');
  link.href = currentObjectUrl;
  // create a filename from input (sanitize)
  const safe = inputText.value.trim().replace(/[^a-z0-9\-_.]/gi, '_').slice(0, 40) || 'qr';
  link.download = `${safe}.png`;
  document.body.appendChild(link);
  link.click();
  link.remove();
}

// Open in new tab
function openInNewTab() {
  if (!currentObjectUrl) return;
  window.open(currentObjectUrl, '_blank');
}

// Events
generateBtn.addEventListener('click', (e) => {
  e.preventDefault();
  generateQr();
});

downloadBtn.addEventListener('click', (e) => {
  e.preventDefault();
  downloadCurrentImage();
});

openBtn.addEventListener('click', (e) => {
  e.preventDefault();
  openInNewTab();
});

// Convenience: generate on Ctrl+Enter
inputText.addEventListener('keydown', (e) => {
  if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
    e.preventDefault();
    generateQr();
  }
});

// Cleanup on unload
window.addEventListener('beforeunload', () => {
  revokeCurrentUrl();
});
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Related Projects

Day 10 : Typing Speed Test

Measures typing speed and accuracy.

Concepts: Timers, string comparison, event listeners.

Day 14 : Image Gallery with Modal

Displays images in a grid with a pop-up modal view.

Concepts: DOM traversal, event bubbling.

Day 15 : Light/Dark Mode Toggle

Switch between light and dark themes with one click (and remember the choice).

Concepts: CSS variables, LocalStorage.