Proposal vs quote: which one a freelancer sends, and how each page is laid out

A quote gives a price for work that is already clear. A proposal explains the work and makes the case for you. Both fit on a page you can send as a link.

A quote is a price for work that is already defined. A proposal explains how you will solve the client's problem and includes the price. If the client has already chosen you and only needs a number, send a quote. If they are still deciding, send a proposal.

That is the whole difference, and the rest is layout. Here is a quote, laid out as one phone-sized page. The price and the valid-until date come first, and the items sit below.

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>Quote Q-2026-014</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; padding: 18px 14px 28px; background: #f4f5f7; color: #1d2330;
    font: 16px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
  .sheet { max-width: 600px; margin: 0 auto; background: #fff; border-radius: 12px;
    padding: 22px 20px; box-shadow: 0 4px 16px rgba(0,0,0,.08); }
  .top { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
  h1 { font-size: 22px; margin: 0 0 2px; }
  .muted { color: #5b6472; font-size: 14px; }
  .box { margin: 16px 0; padding: 14px 16px; border-radius: 10px; background: #eefaf2; border: 1px solid #bfe5cc; }
  .box .big { font-size: 30px; font-weight: 700; color: #0f5132; line-height: 1.1; }
  table { width: 100%; border-collapse: collapse; margin-top: 4px; }
  th, td { text-align: left; padding: 9px 4px; border-bottom: 1px solid #e6e8ee; vertical-align: top; font-size: 15px; }
  th { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: #5b6472; }
  td.n, th.n { text-align: right; white-space: nowrap; }
  tfoot td { font-weight: 700; border-bottom: 0; }
  h2 { font-size: 15px; margin: 20px 0 6px; }
  ul { margin: 0; padding-left: 20px; }
  .accept { margin-top: 18px; padding: 12px 14px; border-radius: 10px; background: #f4f5f7; font-size: 15px; }
</style>
</head>
<body>
<div class="sheet">
  <div class="top">
    <div>
      <h1>Quote Q-2026-014</h1>
      <div class="muted">From Noor Calder, freelance web designer<br>For Juniper Row Cafe</div>
    </div>
    <div class="muted">Issued 1 Oct 2026<br><b>Valid until 15 Oct 2026</b></div>
  </div>

  <div class="box">
    <div class="muted">Fixed price for the work below</div>
    <div class="big" id="total">$0</div>
    <div class="muted" id="deposit"></div>
  </div>

  <table>
    <thead><tr><th>Item</th><th class="n">Qty</th><th class="n">Price</th></tr></thead>
    <tbody id="rows"></tbody>
    <tfoot><tr><td colspan="2">Total</td><td class="n" id="total2"></td></tr></tfoot>
  </table>

  <h2>Not included</h2>
  <ul>
    <li>Writing the text and taking photos</li>
    <li>Changes beyond the two rounds listed above</li>
  </ul>

  <h2>Payment</h2>
  <p style="margin:0">Deposit to start, the rest when the site goes live. Invoice due 14 days after it is sent.</p>

  <div class="accept"><b>To accept:</b> reply to the email with the word "Accepted" before the valid-until date.</div>
</div>

<script>
  // Change the numbers here
  const DEPOSIT_PERCENT = 30;
  const ITEMS = [
    { name: 'Home page, designed and built', qty: 1, price: 900 },
    { name: 'Menu page you can edit yourself', qty: 1, price: 350 },
    { name: 'Contact and map section', qty: 1, price: 150 },
    { name: 'Domain and hosting setup', qty: 1, price: 80 },
    { name: 'Two rounds of changes', qty: 1, price: 0 },
  ];

  const money = (n) => '$' + n.toLocaleString('en-US');
  let total = 0;
  const rows = document.getElementById('rows');
  ITEMS.forEach((it) => {
    const line = it.qty * it.price;
    total += line;
    const tr = document.createElement('tr');
    tr.innerHTML = '<td></td><td class="n"></td><td class="n"></td>';
    tr.children[0].textContent = it.name;
    tr.children[1].textContent = it.qty;
    tr.children[2].textContent = line === 0 ? 'included' : money(line);
    rows.appendChild(tr);
  });
  document.getElementById('total').textContent = money(total);
  document.getElementById('total2').textContent = money(total);
  document.getElementById('deposit').textContent =
    'Deposit ' + DEPOSIT_PERCENT + '%: ' + money(Math.round(total * DEPOSIT_PERCENT / 100));
</script>
</body>
</html>
A quote on one screen. Edit the numbers at the top of the script and the total and deposit update.

Bonsai's guide describes a quote as a document focused on pricing, with a set amount valid for a specified period. That is why the example shows a valid-until date next to the price.

Make it with AI

Paste this prompt into ChatGPT, Claude or Gemini. Change the first line to "quote" or "proposal", and put your facts at the bottom.

Write ONE self-contained HTML file: a freelance [quote OR proposal], read on a phone.
Rules:
- Plain HTML, CSS and JavaScript in one file. No images, no external files, no form that sends data.
- One column, a viewport meta tag, text at least 16px.
- QUOTE: the fixed price and the valid-until date first, then line items, Not included, Payment, How to accept. Fit one screen where possible.
- PROPOSAL: sections in this order: The problem, What I will do, Timeline, Options and prices, About me. Add a small menu to jump between sections.
- Put every number (prices, deposit percent, dates) in one block at the top of the script, with the comment "Change the numbers here".
- If I did not give you a fact, write TODO. Do not invent clients, past work or prices.
My facts:
Client: ...
Work and prices: ...
Valid until: ...
Not included: ...
Payment terms: ...

Then check the result the way the client will.

  • Every number. Item prices, the total, the deposit and the dates, against your own notes.
  • Every TODO. Replace it or delete the line.
  • Invented details. Past clients, results or promises you did not give it.
  • The wording. Is the price called a fixed price or an estimate, and is that what you mean?
  • Your phone. Open it there before anyone else does.
From the AI chat to a link in four steps.
From the AI chat to a link in four steps.

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

The page renders as written, scripts included, and anyone with the link can open it in a browser, on a phone too, with no account. They can read it but not edit it.

Put the link in the body of your email, with one line saying what it is. The client taps it and reads, with nothing to download.

If you change a price or a date later, edit the numbers in the code. The same link then shows the new version, so add a dated line such as "Price updated 12 Oct" to tell the client.

For more on writing each one, see sending a quote to a client and sending a proposal to a client. Quotes that sit next to a price list are covered in HTML for pricing sheets.

What goes in a proposal instead

A proposal does more work. Bonsai lists its parts as an introduction that restates the problem, scope and deliverables, a timeline, pricing, and terms. The example below follows that order, with a short menu at the top so the client can jump around on a phone.

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>Proposal for Juniper Row Cafe</title>
<style>
  * { box-sizing: border-box; }
  html { scroll-behavior: smooth; }
  body { margin: 0; background: #f4f5f7; color: #1d2330;
    font: 16px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif; }
  nav { position: sticky; top: 0; z-index: 2; background: #fff; border-bottom: 1px solid #e1e4ea;
    display: flex; gap: 6px; padding: 8px 12px; overflow-x: auto; }
  nav a { flex: none; padding: 6px 12px; border-radius: 999px; background: #eef1f5;
    color: #1d2330; text-decoration: none; font-size: 14px; }
  main { max-width: 640px; margin: 0 auto; padding: 16px 14px 40px; }
  section { background: #fff; border-radius: 12px; padding: 18px 18px 16px; margin-bottom: 14px;
    box-shadow: 0 3px 12px rgba(0,0,0,.06); scroll-margin-top: 56px; }
  h1 { font-size: 24px; margin: 0 0 4px; }
  h2 { font-size: 18px; margin: 0 0 8px; }
  .muted { color: #5b6472; font-size: 14px; }
  ol, ul { margin: 0; padding-left: 20px; }
  li { margin-bottom: 4px; }
  .step { display: grid; grid-template-columns: 76px 1fr; gap: 10px; padding: 8px 0; border-bottom: 1px solid #eef0f4; }
  .step:last-child { border-bottom: 0; }
  .step b { color: #1d4ed8; }
  .opts { display: grid; gap: 10px; }
  .opt { display: block; border: 2px solid #e1e4ea; border-radius: 10px; padding: 12px 14px; cursor: pointer; }
  .opt input { margin-right: 8px; }
  .opt.on { border-color: #0f5132; background: #eefaf2; }
  .opt .p { float: right; font-weight: 700; }
  .opt small { display: block; color: #5b6472; margin-left: 24px; }
  .sum { margin-top: 12px; padding: 12px 14px; border-radius: 10px; background: #1d2330; color: #fff; }
  .sum b { font-size: 20px; }
</style>
</head>
<body>
<nav>
  <a href="#problem">Problem</a><a href="#plan">Plan</a><a href="#timeline">Timeline</a>
  <a href="#options">Options</a><a href="#about">About</a>
</nav>
<main>
  <section>
    <h1>A website the cafe can keep up to date</h1>
    <div class="muted">Proposal for Juniper Row Cafe, from Noor Calder, 1 Oct 2026</div>
  </section>

  <section id="problem">
    <h2>What you told me</h2>
    <p style="margin:0">Customers look for the menu and opening hours on their phones. Today the menu is a photo of a printed sheet, and the hours differ between your listings. You want one page that is right, that you can change yourself.</p>
  </section>

  <section id="plan">
    <h2>What I will do</h2>
    <ol>
      <li>Design a home page that shows hours, address and the menu without scrolling past a banner.</li>
      <li>Build a menu page whose prices you can edit.</li>
      <li>Add a contact and map section, and set up the domain and hosting.</li>
      <li>Show you how to change the menu in ten minutes.</li>
    </ol>
    <p class="muted" style="margin:10px 0 0">Not included: writing the text and taking photos.</p>
  </section>

  <section id="timeline">
    <h2>Timeline</h2>
    <div class="step"><b>Week 1</b><span>You send the menu, hours and logo. I send a first design.</span></div>
    <div class="step"><b>Week 2</b><span>Your comments, then the build.</span></div>
    <div class="step"><b>Week 3</b><span>Second round of changes, then launch.</span></div>
  </section>

  <section id="options">
    <h2>Three ways to do it</h2>
    <div class="opts" id="opts"></div>
    <div class="sum" id="sum"></div>
    <p class="muted" style="margin:10px 0 0">Option A is the same work and price as quote Q-2026-014. Prices are fixed for the work listed, valid until 15 Oct 2026.</p>
  </section>

  <section id="about">
    <h2>About me</h2>
    <p style="margin:0">I am a freelance web designer. I build small sites for cafes and shops, one page at a time. Reach me at noor@example.com.</p>
  </section>
</main>

<script>
  // Change the numbers here
  const OPTIONS = [
    { id: 'A', name: 'Essentials', price: 1480, weeks: 3, text: 'Home, menu and contact pages.' },
    { id: 'B', name: 'Plus', price: 1980, weeks: 4, text: 'Essentials, plus a photo gallery and an events page.' },
    { id: 'C', name: 'Plus and care', price: 2580, weeks: 4, text: 'Plus, and three months of small updates by me.' },
  ];

  const box = document.getElementById('opts');
  const sum = document.getElementById('sum');
  const money = (n) => '$' + n.toLocaleString('en-US');

  function pick(id) {
    const o = OPTIONS.find((x) => x.id === id);
    box.querySelectorAll('.opt').forEach((l) => l.classList.toggle('on', l.dataset.id === id));
    sum.innerHTML = 'Your choice: <b></b><br><span class="muted" style="color:#c9cfdb"></span>';
    sum.querySelector('b').textContent = 'Option ' + o.id + ', ' + money(o.price);
    sum.querySelector('span').textContent = o.weeks + ' weeks to launch';
  }

  OPTIONS.forEach((o) => {
    const label = document.createElement('label');
    label.className = 'opt';
    label.dataset.id = o.id;
    label.innerHTML = '<span class="p"></span><input type="radio" name="opt"><b></b><small></small>';
    label.querySelector('.p').textContent = money(o.price);
    label.querySelector('b').textContent = o.name;
    label.querySelector('small').textContent = o.text;
    label.querySelector('input').addEventListener('change', () => pick(o.id));
    box.appendChild(label);
  });

  box.querySelector('input').checked = true;
  pick('A');
</script>
</body>
</html>
A proposal in sections. Pick an option to see the choice summary change.

Its price section holds three options. Option A is the same work and price as the quote above, which is how a proposal can contain a quote.

The two layouts side by side. A quote answers how much and until when. A proposal answers why you and what will happen.
The two layouts side by side. A quote answers how much and until when. A proposal answers why you and what will happen.

Which one to send

Bonsai's guide suggests quotes for straightforward, well-defined tasks with fixed pricing. It suggests proposals for complex projects, several options, or a client who needs persuading.

One question decides it. Is the work clear, and has the client already chosen you?
One question decides it. Is the work clear, and has the client already chosen you?

Be careful with the words. Sources do not agree on them. One freelance designer sends an early, flexible "quote" first and a fixed-price proposal after the client approves it. So whichever name you use, write on the page whether the number is a fixed price or an estimate.

The two compared

Quote Proposal
Main question it answers How much, and until when Why you, and what will happen
Typical length One screen Several short sections
Price Fixed, with a valid-until date One section, often with options
Best when Work is clear, client has chosen you Work needs explaining, client is comparing
Can hold the other No Yes, a quote as the price section

Ways to send either one

Attached file Text in the email Link to a page
Opens on a phone After a download Yes Yes, in the browser
Change after sending Send a new file Send a new email Edit the page, same link
Layout kept Yes Plain text Yes
Client needs an account No No No

If you also need a copy for your records, keep the finished page as a PDF too. For the choice between the two formats, see email vs link for documents.

When it does not work

What you see Cause Fix
The chat shows code, not a page You copied the chat text, not the code block Copy only the code, from the doctype line to the closing html tag
A logo or photo is missing Images from other sites are blocked Ask for no images, or a logo drawn in SVG
The total is wrong The AI guessed or broke the script Recheck the numbers block and ask it to fix the sum
A contact form does nothing Forms cannot send to a server from the link Write your email address as plain text
The client quotes an old price They are looking at an old file Send only the link, never the file
The text is tiny on a phone No viewport meta tag Ask the AI to add one and a 16px base size

Questions people ask

Can a proposal include a quote?

Yes. Bonsai's guide says a proposal often includes a quote as its pricing part. In the example on this page, option A in the proposal is the same work and price as the separate quote.

Is a quote the same as an estimate?

The words are not used the same way everywhere. Bonsai describes a quote as a set amount valid for a period, while one freelance designer calls an early, flexible price a "quote". Write "fixed price" or "estimate" on the page itself.

Which should I send first?

If the client has already chosen you and the work is clear, send a quote. If they are still deciding, or the work needs explaining, send a proposal. When unsure, a one-screen proposal with the price near the top covers both.

How long should each be?

A quote usually fits one screen on a phone: price, items, what is not included, payment, how to accept. A proposal runs to several short sections. Length is not a goal. Stop when the client has what they need to say yes.

Can the client edit my quote or proposal page?

No. Anyone with the share link can open and read it, but only you can change it, by editing the code in your NOS document. The link stays the same after your edit.

Keep reading