How to make a bakery menu page you can put in your bio

A bakery menu does not need menu software. One page with each item, its price and a line about it can sit behind the link in your bio, and you change a price by editing the page.

A bakery menu on the web is one page that lists each item with its price, a line about what it is, what it contains, and when you are open.

Put the page at a link, put the link in your bio, and anyone who taps it sees the same list you would hand across the counter.

You do not need menu software for that. The page is plain HTML, and an AI chat can write it from your price list. Below is a made-up bakery. Tap the section buttons, then tick Hide sold out items.

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>Marrow &amp; Rye Bakery - Menu</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; font-family: Georgia, 'Times New Roman', serif; background: #fbf6ee; color: #3a2a1c; line-height: 1.5; }
  .wrap { max-width: 560px; margin: 0 auto; padding: 20px 16px 36px; }
  header { text-align: center; padding: 8px 0 14px; }
  header svg { width: 44px; height: 44px; }
  h1 { font-size: 26px; margin: 4px 0 2px; letter-spacing: .5px; }
  .sub { margin: 0; font: 14px/1.4 system-ui, sans-serif; color: #7a6552; }
  .today { background: #3a2a1c; color: #fbf6ee; border-radius: 10px; padding: 10px 14px; margin: 10px 0 14px; font: 14px/1.45 system-ui, sans-serif; }
  .today b { color: #f2c46b; }
  nav { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
  nav a { font: 600 14px system-ui, sans-serif; color: #3a2a1c; background: #f0e3cf; padding: 8px 14px; border-radius: 99px; text-decoration: none; }
  .opt { font: 14px system-ui, sans-serif; margin: 10px 0 0; color: #5d4936; }
  .opt input { width: 18px; height: 18px; vertical-align: -3px; margin-right: 6px; }
  h2 { font-size: 20px; margin: 26px 0 4px; padding-bottom: 6px; border-bottom: 2px solid #e5d3b8; scroll-margin-top: 8px; }
  .item { padding: 12px 0; border-bottom: 1px dashed #e0cfb4; }
  .row { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
  .name { font-weight: 700; font-size: 17px; }
  .price { font: 700 16px system-ui, sans-serif; white-space: nowrap; }
  .look { margin: 2px 0 0; font-size: 15px; color: #5d4936; }
  .tags { margin: 4px 0 0; font: 13px/1.4 system-ui, sans-serif; color: #7a6552; }
  .sold { opacity: .55; }
  .sold .name::after { content: " - sold out today"; font: 600 13px system-ui, sans-serif; color: #b4472a; }
  .hidden { display: none; }
  footer { margin-top: 26px; font: 14px/1.5 system-ui, sans-serif; color: #5d4936; background: #f0e3cf; border-radius: 10px; padding: 12px 14px; }
  footer p { margin: 0 0 6px; }
  footer p:last-child { margin: 0; }
</style>
</head>
<body>
<div class="wrap">
  <header>
    <svg viewBox="0 0 44 44" aria-hidden="true"><circle cx="22" cy="22" r="21" fill="#f0e3cf"/><path d="M22 34V14M22 20c-4 0-6-3-6-6 4 0 6 3 6 6zm0 0c4 0 6-3 6-6-4 0-6 3-6 6zm0 7c-4 0-6-3-6-6 4 0 6 3 6 6zm0 0c4 0 6-3 6-6-4 0-6 3-6 6z" fill="none" stroke="#8a5a2b" stroke-width="2" stroke-linecap="round"/></svg>
    <h1>Marrow &amp; Rye Bakery</h1>
    <p class="sub">Menu and prices. Last updated Friday 2 October.</p>
  </header>

  <div class="today"><b>Today:</b> the cardamom buns are out by 8 a.m. and usually gone by noon. Cakes need two days' notice.</div>

  <nav>
    <a href="#bread">Bread</a>
    <a href="#pastry">Pastry</a>
    <a href="#cakes">Cakes</a>
    <a href="#drinks">Drinks</a>
  </nav>
  <p class="opt"><label><input type="checkbox" id="hide"> Hide sold out items</label></p>

  <h2 id="bread">Bread</h2>
  <div class="item">
    <div class="row"><span class="name">Rye sourdough loaf</span><span class="price">$7.50</span></div>
    <p class="look">Dark, crackly crust with a dense, slightly sour crumb. Slices well for toast.</p>
    <p class="tags">Contains: wheat, rye</p>
  </div>
  <div class="item sold">
    <div class="row"><span class="name">Seeded rolls, box of 4</span><span class="price">$5.50</span></div>
    <p class="look">Small round rolls covered in sesame and poppy seeds.</p>
    <p class="tags">Contains: wheat, sesame</p>
  </div>

  <h2 id="pastry">Pastry</h2>
  <div class="item">
    <div class="row"><span class="name">Cardamom bun</span><span class="price">$3.80</span></div>
    <p class="look">Twisted into a knot, brushed with sugar syrup. Sticky outside, soft inside.</p>
    <p class="tags">Contains: wheat, milk, egg</p>
  </div>
  <div class="item">
    <div class="row"><span class="name">Butter croissant</span><span class="price">$3.50</span></div>
    <p class="look">Flaky layers that shatter on the first bite, about the size of your palm.</p>
    <p class="tags">Contains: wheat, milk</p>
  </div>
  <div class="item sold">
    <div class="row"><span class="name">Plum frangipane tart</span><span class="price">$4.90</span></div>
    <p class="look">A small tart with a ring of plum slices on an almond filling.</p>
    <p class="tags">Contains: wheat, milk, egg, almond</p>
  </div>

  <h2 id="cakes">Cake (order two days ahead)</h2>
  <div class="item">
    <div class="row"><span class="name">Honey and lemon cake, 8 slices</span><span class="price">$28.00</span></div>
    <p class="look">A round cake with white glaze and thin lemon rounds on top.</p>
    <p class="tags">Contains: wheat, milk, egg</p>
  </div>

  <h2 id="drinks">Drinks</h2>
  <div class="item">
    <div class="row"><span class="name">Filter coffee</span><span class="price">$2.80</span></div>
    <p class="look">Served in a white mug. One free refill.</p>
  </div>

  <footer>
    <p><b>Open:</b> Tuesday to Saturday, 7 a.m. to 3 p.m. Closed Sunday and Monday.</p>
    <p><b>Find us:</b> 14 Alder Lane, Fernwick.</p>
    <p><b>Orders:</b> send us a message on Instagram or call 555-0142.</p>
    <p>Allergens are listed per item. We bake nuts and sesame in the same kitchen, so ask us if you need to be sure.</p>
  </footer>
</div>

<script>
  // Show or hide the items marked with the class "sold"
  const box = document.getElementById('hide');
  box.addEventListener('change', () => {
    document.querySelectorAll('.sold').forEach((el) => {
      el.classList.toggle('hidden', box.checked);
    });
  });
</script>
</body>
</html>
A bakery menu as one page. Section buttons jump down the page, and sold out items can be hidden.

What goes on the page

A menu that gets used is short on decoration and complete on facts. These are the parts the demo above has, in the order a customer looks for them:

  1. Sections as buttons at the top, so someone after bread does not scroll past the cakes.
  2. Name and price on one line, with the price lined up at the right.
  3. One line about the item: size, crust, filling, what it is good for.
  4. Contains: the allergens you are sure of, per item.
  5. A note for today: what sells out early, what needs ordering ahead.
  6. Hours, address and how to order at the bottom.

Describe the photos in words

Most bakeries have good photos of their bread. They will not appear on this page. Images that load from another address are blocked in NOS, so a picture copied from a website or a social post shows as an empty box.

A picture from another address is blocked and leaves an empty box. A short description always shows.
A picture from another address is blocked and leaves an empty box. A short description always shows.

Words do the job well here. Say how big it is, what the crust is like, what is inside, and what it goes with.

If you want a little decoration, shapes drawn with CSS or inline SVG are part of the page itself, like the wheat mark in the demo.

Make it with AI

Give the chat your real list and tell it not to add anything. Paste this into ChatGPT, Claude or Gemini, and replace the square brackets with your own details:

Write one self-contained HTML page for a small bakery menu. Use only HTML, CSS and a little JavaScript. No images and no external files.

Bakery: [name], [town]. Open [days and hours]. Orders: [how to order].

Sections and items (name, price, one line about it, what it contains):
Bread:
- [item], [price], [one line], [allergens]
Pastry:
- [item], [price], [one line], [allergens]

Rules: it must read well on a phone. Put the section names as buttons at the top that jump to each section. Show the price on the same line as the name. Add a checkbox that hides items marked sold out. End with the hours, address and how to order. Do not invent any item, price or allergen. If something is missing, leave it out.

Then check the result against your own list. The AI writes confidently, and a wrong price on a menu is yours to answer for.

  • Every price matches your list, and the dollar signs and decimals are right.
  • Every allergen line says only what you told it. Add or correct them yourself.
  • Hours and address are exactly as you gave them.
  • No extra items were added to make a section look fuller.
  • On a phone the buttons are easy to tap and nothing runs off the side.

Paste the finished page into NOS and choose Create share link. HTML to link walks through it.

The link opens in a browser, on a phone too, with no account and nothing to install. The people who open it can read the page but cannot edit it.

When a price changes, edit the code in NOS. The link stays the same, so your bio, your printed QR code and any message you already sent all show the new version. Someone who already has the page open needs to reload it.

From your price list to the link in your bio, in four steps.
From your price list to the link in your bio, in four steps.

Then put the link where customers look. As of October 2026, these two places take a web address:

  • Instagram: open your profile, choose Edit profile, and add the address as your website. Instagram's help page covers it. The screens can change, so follow the app if it differs.
  • Google Business Profile: under Edit profile, Contact, there is a Menu link field. Google's help page describes it.

For a counter or a window, turn the same address into a QR code. QR code link shows how.

What a customer sees: your profile with the link, then the menu page one tap later.
What a customer sees: your profile with the link, then the menu page one tap later.

Ways to put a menu in front of customers

Photo of the printed menu PDF file Page at a link
Change a price Take and post a new photo Edit and send the file again Edit the page, same link
Text readable on a phone Only if the photo is sharp Laid out for paper, may need zooming Fits the screen
Sold out today Not shown Not shown Mark the item
Old copies in circulation Yes Yes No, one address

If you already have a printed menu, a page does not replace it. It gives the customer who is not in the shop the same information. Menu link covers the restaurant version of the same idea.

A second page: pre-orders

Many bakeries take orders for Saturday loaves or a cake. This page lets a customer pick quantities and see the total.

A form inside the page works, but sending it to a server does not, so the page writes the order as a message. The customer copies it and sends it to you.

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>Marrow &amp; Rye Bakery - Pre-order list</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; font-family: system-ui, sans-serif; background: #fbf6ee; color: #3a2a1c; line-height: 1.5; }
  .wrap { max-width: 560px; margin: 0 auto; padding: 18px 16px 30px; }
  h1 { font: 700 22px Georgia, serif; margin: 0 0 4px; }
  .lead { margin: 0 0 14px; font-size: 14px; color: #6b5744; }
  .line { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: center; padding: 10px 0; border-bottom: 1px dashed #e0cfb4; }
  .nm { font-weight: 600; }
  .pr { font-size: 14px; color: #6b5744; }
  .qty { display: flex; align-items: center; gap: 6px; }
  .qty button { width: 36px; height: 36px; border-radius: 50%; border: 1px solid #cdb592; background: #fff; font-size: 20px; line-height: 1; color: #3a2a1c; cursor: pointer; }
  .qty output { min-width: 22px; text-align: center; font-weight: 700; }
  .total { display: flex; justify-content: space-between; font-weight: 700; font-size: 17px; padding: 14px 0 6px; }
  label { display: block; font-size: 14px; font-weight: 600; margin: 12px 0 4px; }
  input[type=text], input[type=date] { width: 100%; padding: 10px; font-size: 16px; border: 1px solid #cdb592; border-radius: 8px; background: #fff; }
  .go { margin-top: 14px; width: 100%; padding: 12px; font-size: 16px; font-weight: 700; color: #fff; background: #8a5a2b; border: 0; border-radius: 10px; cursor: pointer; }
  .msg { margin-top: 14px; display: none; }
  .msg.on { display: block; }
  textarea { width: 100%; height: 150px; padding: 10px; font: 15px/1.45 system-ui, sans-serif; border: 1px solid #cdb592; border-radius: 8px; background: #fff; resize: vertical; }
  .hint { font-size: 13px; color: #6b5744; margin: 6px 0 0; }
  .warn { color: #b4472a; font-size: 14px; margin: 8px 0 0; min-height: 20px; }
</style>
</head>
<body>
<div class="wrap">
  <h1>Pre-order for Saturday</h1>
  <p class="lead">Pick what you want, then copy the message and send it to us on Instagram or by text. We confirm by reply.</p>

  <form id="f">
    <div class="line" data-name="Rye sourdough loaf" data-price="7.50">
      <span class="nm">Rye sourdough loaf</span><span class="pr">$7.50</span>
      <span class="qty"><button type="button" data-d="-1" aria-label="One fewer rye loaf">-</button><output>0</output><button type="button" data-d="1" aria-label="One more rye loaf">+</button></span>
    </div>
    <div class="line" data-name="Cardamom bun" data-price="3.80">
      <span class="nm">Cardamom bun</span><span class="pr">$3.80</span>
      <span class="qty"><button type="button" data-d="-1" aria-label="One fewer cardamom bun">-</button><output>0</output><button type="button" data-d="1" aria-label="One more cardamom bun">+</button></span>
    </div>
    <div class="line" data-name="Butter croissant" data-price="3.50">
      <span class="nm">Butter croissant</span><span class="pr">$3.50</span>
      <span class="qty"><button type="button" data-d="-1" aria-label="One fewer croissant">-</button><output>0</output><button type="button" data-d="1" aria-label="One more croissant">+</button></span>
    </div>
    <div class="line" data-name="Honey and lemon cake" data-price="28.00">
      <span class="nm">Honey and lemon cake</span><span class="pr">$28.00</span>
      <span class="qty"><button type="button" data-d="-1" aria-label="One fewer lemon cake">-</button><output>0</output><button type="button" data-d="1" aria-label="One more lemon cake">+</button></span>
    </div>

    <div class="total"><span>Total</span><span id="total">$0.00</span></div>

    <label for="who">Name for the order</label>
    <input type="text" id="who" autocomplete="off">
    <label for="when">Pick-up date</label>
    <input type="date" id="when">

    <button class="go" type="submit">Make my message</button>
    <p class="warn" id="warn" role="status"></p>
  </form>

  <div class="msg" id="msg">
    <label for="out">Your message (select it, copy it, send it)</label>
    <textarea id="out" readonly></textarea>
    <p class="hint">Nothing has been sent. This page only writes the text for you.</p>
  </div>
</div>

<script>
  const lines = [...document.querySelectorAll('.line')];
  const money = (n) => '$' + n.toFixed(2);

  function sum() {
    let t = 0;
    lines.forEach((l) => { t += Number(l.dataset.price) * Number(l.querySelector('output').textContent); });
    document.getElementById('total').textContent = money(t);
    return t;
  }

  // + and - buttons change the count on their own line
  lines.forEach((l) => {
    l.querySelectorAll('button').forEach((b) => {
      b.addEventListener('click', () => {
        const o = l.querySelector('output');
        o.textContent = Math.max(0, Number(o.textContent) + Number(b.dataset.d));
        sum();
      });
    });
  });

  document.getElementById('f').addEventListener('submit', (e) => {
    e.preventDefault();  // keep the page here; nothing is sent anywhere
    const warn = document.getElementById('warn');
    const who = document.getElementById('who').value.trim();
    const when = document.getElementById('when').value;
    const picked = lines.filter((l) => Number(l.querySelector('output').textContent) > 0);
    if (!picked.length || !who || !when) {
      warn.textContent = 'Pick at least one item and fill in your name and the date.';
      document.getElementById('msg').classList.remove('on');
      return;
    }
    warn.textContent = '';
    const text = picked.map((l) => l.querySelector('output').textContent + ' x ' + l.dataset.name).join('\n');
    const out = document.getElementById('out');
    out.value = 'Hi, I would like to pre-order for ' + when + ':\n' + text + '\nTotal: ' + money(sum()) + '\nName: ' + who;
    document.getElementById('msg').classList.add('on');
    out.focus();
    out.select();
  });
</script>
</body>
</html>
Pick items, add a name and a date, and the page writes a message to copy. Nothing is sent from the page.

Prices live in two places in this page, the visible price and a data attribute on each line. Change both, or ask the AI to change them for you.

When it does not work

What you see Cause Fix
A photo shows as an empty box The picture loads from another address, which is blocked Describe the item in words instead
The page looks tiny on a phone The viewport meta tag is missing Ask the AI to add it
The page shows an old price The visitor still has the old page open Reload the page
Items missing after you pasted The chat cut the answer off Ask it to continue, then paste the whole page
The section buttons do nothing The ids do not match the button links Ask the AI to match each button to its section
An allergen line is wrong The AI guessed Correct it by hand, and compare with your own list

If a page looks wrong on a phone, HTML not loading images goes through the usual causes.

Questions people ask

Do I need menu software to put a bakery menu online?

Not for a list of items and prices. A single HTML page does that, and an AI chat can write it from your price list. Software becomes worth looking at when you need things a plain page cannot do, such as taking payment or sending each order to you automatically.

Can I put photos of my bread on the page?

Not by linking to a picture stored somewhere else. In NOS, images that load from another address are blocked, so a photo copied from a website or social post shows as an empty box. Describe each item in a short line instead, or draw simple shapes with CSS or inline SVG.

How do I mark an item as sold out?

Edit the page and mark that item, or ask the AI to do it. In the demo above, one class name on the item greys it out and the checkbox can hide it. After you edit, the same link shows the new version. Someone who already has the page open needs to reload it.

Can customers order from this page?

The menu page only shows items and prices. A form in the page can add up an order, as in the pre-order demo, but sending it to a server is blocked. The page writes a message, and the customer sends it to you themselves by message or text.

Where else can I use the same link?

Anywhere a web address goes: your Instagram website field, the menu link field in your Google Business Profile, a text message, or a QR code printed for the counter. They all open the same page, so a price change reaches all of them at once.

Keep reading