Make a restaurant menu QR code you can change later

The code only holds an address. Put your menu on a page at that address, and you can edit dishes and prices whenever you like without printing a new code.

A restaurant menu QR code is two separate things: a menu page at a web address, and a code that holds that address. Build the page first, copy its link, then turn the link into a code.

The code never has to change, because only the page behind it does.

Here is a complete menu page to start from. It has tappable section names, a vegetarian filter, a Today box and one sold-out dish. Scroll it, tap a section, tick the filter.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Harbor Lane Kitchen - Menu</title>
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0; font-family: system-ui, sans-serif; font-size: 17px; line-height: 1.5;
    color: #1f2933; background: #fffdf8;
  }
  header { padding: 22px 18px 10px; text-align: center; }
  header h1 { margin: 0; font-size: 26px; letter-spacing: .01em; }
  header p { margin: 4px 0 0; color: #6b7280; font-size: 15px; }
  nav {
    position: sticky; top: 0; z-index: 2; display: flex; gap: 8px; overflow-x: auto;
    padding: 10px 14px; background: #fffdf8; border-bottom: 1px solid #eee6d6;
  }
  nav a {
    flex: none; padding: 8px 14px; border-radius: 99px; background: #f3ead8;
    color: #5b4a22; text-decoration: none; font-weight: 600; font-size: 15px;
  }
  main { max-width: 640px; margin: 0 auto; padding: 4px 18px 40px; }
  .filter { display: flex; align-items: center; gap: 8px; margin: 14px 0 0; font-size: 15px; color: #374151; }
  .filter input { width: 20px; height: 20px; }
  section { padding-top: 6px; }
  h2 { margin: 22px 0 6px; font-size: 20px; color: #7a4b12; }
  .today { background: #fff3d6; border: 1px solid #f0d9a0; border-radius: 12px; padding: 4px 14px 8px; margin-top: 16px; }
  .today h2 { margin-top: 10px; }
  .dish { display: flex; justify-content: space-between; gap: 14px; padding: 10px 0; border-bottom: 1px solid #f0e8d8; }
  .dish:last-child { border-bottom: 0; }
  .name { font-weight: 600; }
  .desc { display: block; font-weight: 400; font-size: 15px; color: #6b7280; }
  .price { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
  .tag { display: inline-block; margin-left: 4px; padding: 0 7px; border-radius: 6px; background: #e8efe6; color: #2f5d34; font-size: 12px; font-weight: 600; font-style: normal; }
  .sold { opacity: .55; }
  .sold .price { text-decoration: line-through; }
  .sold .name::after { content: "  Sold out"; font-size: 13px; color: #b45309; font-weight: 700; }
  .hide { display: none; }
</style>
</head>
<body>
<header>
  <h1>Harbor Lane Kitchen</h1>
  <p>Menu, updated Thursday</p>
</header>

<nav>
  <a href="#today">Today</a>
  <a href="#starters">Starters</a>
  <a href="#mains">Mains</a>
  <a href="#desserts">Desserts</a>
  <a href="#drinks">Drinks</a>
</nav>

<main>
  <label class="filter"><input type="checkbox" id="veg"> Show vegetarian dishes only</label>

  <div class="today" id="today">
    <h2>Today</h2>
    <div class="dish" data-veg="no"><span class="name">Grilled hake, brown butter<span class="desc">Capers, new potatoes</span></span><span class="price">$24</span></div>
    <div class="dish" data-veg="yes"><span class="name">Fig and burrata <em class="tag">veg</em><span class="desc">Honey, toasted seeds</span></span><span class="price">$14</span></div>
  </div>

  <section id="starters">
    <h2>Starters</h2>
    <div class="dish" data-veg="yes"><span class="name">Tomato soup <em class="tag">veg</em><span class="desc">Basil oil, sourdough</span></span><span class="price">$8</span></div>
    <div class="dish" data-veg="no"><span class="name">Crab cakes<span class="desc">Lemon mayonnaise. Contains shellfish, egg</span></span><span class="price">$13</span></div>
    <div class="dish sold" data-veg="yes"><span class="name">Roasted beet salad <em class="tag">veg</em></span><span class="price">$11</span></div>
  </section>

  <section id="mains">
    <h2>Mains</h2>
    <div class="dish" data-veg="no"><span class="name">Roast chicken<span class="desc">Garlic, rosemary, seasonal greens</span></span><span class="price">$21</span></div>
    <div class="dish" data-veg="yes"><span class="name">Mushroom risotto <em class="tag">veg</em><span class="desc">Parmesan. Contains milk</span></span><span class="price">$18</span></div>
    <div class="dish" data-veg="no"><span class="name">Beef burger<span class="desc">Cheddar, pickles, fries. Contains gluten, milk</span></span><span class="price">$17</span></div>
  </section>

  <section id="desserts">
    <h2>Desserts</h2>
    <div class="dish" data-veg="yes"><span class="name">Lemon tart <em class="tag">veg</em><span class="desc">Contains gluten, egg, milk</span></span><span class="price">$8</span></div>
    <div class="dish" data-veg="yes"><span class="name">Chocolate pot <em class="tag">veg</em><span class="desc">Contains milk</span></span><span class="price">$7</span></div>
  </section>

  <section id="drinks">
    <h2>Drinks</h2>
    <div class="dish" data-veg="yes"><span class="name">Sparkling water</span><span class="price">$3</span></div>
    <div class="dish" data-veg="yes"><span class="name">Fresh lemonade</span><span class="price">$4</span></div>
    <div class="dish" data-veg="yes"><span class="name">Filter coffee</span><span class="price">$3</span></div>
  </section>
</main>

<script>
  const veg = document.getElementById('veg');
  const dishes = document.querySelectorAll('.dish');

  // Hide every dish not marked vegetarian while the box is ticked
  veg.addEventListener('change', () => {
    dishes.forEach((d) => d.classList.toggle('hide', veg.checked && d.dataset.veg !== 'yes'));
  });
</script>
</body>
</html>
A phone-sized menu page. Replace the dishes and prices with yours and keep the structure.

Copy the code into your own file and swap in your restaurant name, dishes and prices. Or let an AI chat do it for you, as below.

Make it with AI

You do not have to write the HTML yourself. Paste this prompt into ChatGPT, Claude or Gemini, and put your own dishes at the bottom.

Make my restaurant menu as ONE complete HTML file for people reading on a phone.
- Page title: [restaurant name]. A line under it: "Menu, updated [day]".
- A sticky row of section links at the top (Today, Starters, Mains, Desserts, Drinks) that jump to each section.
- One line per dish: name on the left, price on the right, a short grey description below.
- Allergens as plain text after the description, for example "Contains milk, egg".
- Body text 17px or larger, white background, dark text.
- A "Today" box at the top that I can edit each morning.
- No external images, fonts or scripts. Everything inside the one file.
- Include the viewport meta tag.
My dishes and prices, one per line (name - price - allergens):
[paste your list here]

Then check the result before you use it:

  • Every dish name and price matches your real menu. An AI can round a price or invent a dish.
  • Allergen text says what your kitchen says, not what the AI guessed.
  • The viewport tag is in the head. MDN explains that without it, a phone may draw the page in a wider virtual window and shrink it, so the text comes out tiny.
  • Pictures are drawn inside the file or left out. Outside images are blocked on a share link.

Paste the HTML into a NOS document and choose Create share link. HTML to link walks through it. You get a web address.

Anyone with it can open the menu in a browser, on a phone or a computer, without an account, and they cannot edit it.

From AI chat to a printed code. The page comes first, the code is made last from the finished link.
From AI chat to a printed code. The page comes first, the code is made last from the finished link.

The page renders as written and its scripts run, so the section links and the vegetarian filter work for your customers. If you change the code later, the same link shows the new version.

Open the link on your own phone before you go further. If it reads badly there, it will read badly at the table. A longer look at the menu itself is in Menu link.

Open your share link in Chrome on a computer. Chrome has a Create QR code option for the page you have open.

As of October 2026, Chrome Help puts it under Cast, save, and share in the Chrome menu, and offers to copy the code or download it. Menu names can change, so search Chrome Help for the option name if yours looks different.

Any other QR code maker also works, as long as the code holds your link directly. Check by scanning it and reading the address your phone shows before the page opens.

Keep the address short if you can. Denso Wave's QR code site says that more data needs more modules and a larger code. A shorter link gives a code with fewer, and so bigger, modules at the same printed size.

Now scan it yourself. On an iPhone, open the Camera app, hold the phone so the code appears, and tap the link.

On a Pixel phone, open Camera, point it at the code and tap the link that appears. Google notes that Camera scan suggestions has to be turned on.

What a customer sees: point the camera, tap the link, read the menu.
What a customer sees: point the camera, tap the link, read the menu.

A QR code needs a clear margin of four modules on every side, and nothing printed inside it. If you place the code on a card, leave that white border. Do not let a frame, a photo or text touch the code.

Denso Wave also says to print as large as the space allows, since bigger modules are easier to read. Put a few words beside the code that say what you get, such as "Today's menu". A bare square invites no one to scan it.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Table card layout</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #eef0f3; color: #1f2933; }
  .wrap { max-width: 760px; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; justify-content: center; }
  .controls { flex: 1 1 250px; max-width: 340px; }
  label { display: block; margin: 0 0 4px; font-size: 14px; font-weight: 600; }
  input[type=text], select { width: 100%; padding: 9px 10px; margin-bottom: 14px; font: inherit; font-size: 16px; border: 1px solid #c9ced6; border-radius: 8px; background: #fff; }
  .hint { font-size: 13px; color: #4b5563; line-height: 1.45; margin: 0; }
  .card {
    flex: 0 0 auto; width: 270px; background: #fff; border-radius: 12px; padding: 22px 20px 18px;
    text-align: center; box-shadow: 0 6px 22px rgba(0, 0, 0, .14);
  }
  .card h1 { margin: 0 0 2px; font-size: 20px; }
  .card .sub { margin: 0 0 14px; font-size: 14px; color: #6b7280; }
  .qrbox {
    position: relative; width: 190px; height: 190px; margin: 0 auto 12px;
    border: 2px dashed #9ca3af; background: #fff; display: grid; place-items: center;
  }
  /* the white margin around a real code is part of the code: keep it clear */
  .qrbox::before { content: ""; position: absolute; inset: 24px; border: 2px solid #1f2933; background: repeating-linear-gradient(45deg, #f3f4f6 0 8px, #fff 8px 16px); }
  .qrbox span { position: relative; z-index: 1; background: #fff; padding: 2px 8px; font-size: 13px; font-weight: 700; }
  .label { margin: 0; font-size: 22px; font-weight: 700; line-height: 1.2; }
  .addr { margin: 8px 0 0; font-size: 13px; color: #4b5563; word-break: break-all; }
</style>
</head>
<body>
<div class="wrap">
  <div class="controls">
    <label for="label">Words beside the code</label>
    <select id="label">
      <option>Today's menu</option>
      <option>Menu and prices</option>
      <option>Order from your table</option>
      <option>See what is on today</option>
    </select>

    <label for="name">Restaurant name</label>
    <input type="text" id="name" value="Harbor Lane Kitchen">

    <label for="addr">Printed address (your share link)</label>
    <input type="text" id="addr" value="https://example.com/your-menu-link">

    <p class="hint">The dashed square is the code plus its clear margin. Nothing is printed inside the dashed line except the code. Replace the striped square with the code you download.</p>
  </div>

  <div class="card" id="card">
    <h1 id="outName">Harbor Lane Kitchen</h1>
    <p class="sub">Scan with your phone camera</p>
    <div class="qrbox"><span>QR code here</span></div>
    <p class="label" id="outLabel">Today's menu</p>
    <p class="addr" id="outAddr">https://example.com/your-menu-link</p>
  </div>
</div>

<script>
  const pairs = [['label', 'outLabel'], ['name', 'outName'], ['addr', 'outAddr']];

  // Copy each field into the matching spot on the card as you type
  pairs.forEach(([from, to]) => {
    const src = document.getElementById(from);
    const out = document.getElementById(to);
    const update = () => { out.textContent = src.value; };
    src.addEventListener('input', update);
    src.addEventListener('change', update);
  });
</script>
</body>
</html>
A table card layout. Change the words and the printed address, then swap the striped square for your downloaded code.

Google's Pixel help advises scanning in good light and avoiding shadows or glare on the code. Take that into account when you pick a spot and a finish for the card. Always test the printed copy, not the screen it was designed on.

This is the reason to use a link. When the hake goes from $24 to $26, you do not reprint anything.

  1. Open the same document in NOS.
  2. Change the price in the code. Or paste the current page into your AI chat and ask: "Change the hake to $26 and mark the risotto as sold out. Return the whole file."
  3. Put the result back in the same document.
One printed code, two different menus. The address stays the same, so nothing is reprinted.
One printed code, two different menus. The address stays the same, so nothing is reprinted.

Visitors who open the link now see the new version. To mark a dish as out, give it the sold class in the demo page above. It greys the dish and strikes through the price.

Ways to put a menu behind a code

The code works with anything that has an address. What changes is what happens on the day a price moves.

What the code points at Changing a price What the customer opens
A menu page at a share link Edit the page. The link stays the same A page in the browser, no account
A PDF file Make a new PDF. The code works only if the new file keeps the old address The file, as laid out when it was made
A photo of the paper menu Take a new photo and replace the old one An image, with no text to search

If you already have a PDF behind your codes, QR code link explains how to move to a page. For what to put first on the page, see Host a menu.

When it does not work

What you see Cause Fix
The phone camera shows nothing Code too small, shadowed, blurry or glared Print larger, light it, hold steady, show the whole square
It scans on screen but not on paper Code printed too small, or the margin was cropped Reprint larger with a four-module white margin
An Android camera offers no link Camera scan suggestions is off Turn it on in Camera settings, or use Quick Settings
The page opens with tiny text The viewport tag is missing Ask the AI to add the viewport meta tag
Pictures are missing Outside images are blocked Draw them inside the file, or drop them
An order form does not send Sending a form to a server is blocked Use the page for reading only
The table still shows an old price You made a new link instead of editing the old document Edit the original document and use its link

Questions people ask

Do I need to pay for a QR code generator?

Not necessarily. Chrome can make a QR code for the page you have open, and it lets you download the code. As of October 2026 that is under Cast, save, and share in the Chrome menu on a computer. Any other generator that makes a plain code for your link works too.

Can I change the menu after the codes are printed?

Yes, as long as the code holds your share link and not a file. The code only stores the address. You edit the page behind the address, and the same link shows the new version.

How big should the printed code be?

Print it as large as the space allows, and keep a clear margin of four modules around it. Then test the printed copy with a phone held the way a customer would hold it.

Does the customer need to install an app?

The iPhone Camera app and the Camera app on a Pixel phone can read a QR code and offer a link to tap. Other phones can differ, so test on a few before you print many.

Can customers order through the menu page?

The page can show a form and handle it inside the page, but sending a form to a server is blocked on a NOS share link. Use the page for reading the menu and keep taking orders the way you do now.

Keep reading