Put a QR code on your paper resume that opens your live page

A QR code on a career-fair resume holds a link, not the resume. Put the resume on a page, turn its link into a code, and everyone who scans lands on the current version.

A resume QR code does not contain your resume. It contains a link, and scanning it opens whatever sits at that link. So the job has two parts: give your resume a link, then print that link as a code on the paper copy.

If you searched for how to create a link for your resume, the first part is below. The second part takes a minute. Someone at a career-fair table scans the corner of your sheet, and your live page opens on their phone.

Four steps from a resume page to a printed code: page, share link, QR code, paper resume.
Four steps from a resume page to a printed code: page, share link, QR code, paper resume.

Start with the page the code will open. This one is written for a phone: who you are, what you want, and details that open on tap.

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>Taylor Morgan - Resume</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1f2430; line-height: 1.5; }
  .wrap { max-width: 680px; margin: 0 auto; padding: 18px 16px 30px; }
  header { background: #fff; border-radius: 14px; padding: 20px 20px 16px; box-shadow: 0 2px 10px rgba(0,0,0,.07); }
  .avatar { width: 52px; height: 52px; border-radius: 50%; background: linear-gradient(135deg, #1d4ed8, #7c3aed); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 20px; float: left; margin-right: 14px; }
  h1 { margin: 0; font-size: 24px; line-height: 1.2; }
  .role { margin: 2px 0 0; font-size: 14px; color: #4b5563; }
  .want { clear: both; margin: 14px 0 0; padding: 10px 12px; background: #eef4ff; border-radius: 10px; font-size: 14px; }
  .links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
  .links a { padding: 8px 12px; border-radius: 99px; border: 1px solid #c7cfdd; font-size: 13px; color: #1d4ed8; text-decoration: none; background: #fff; }
  section { background: #fff; border-radius: 14px; padding: 14px 20px; margin-top: 12px; box-shadow: 0 2px 10px rgba(0,0,0,.07); }
  h2 { margin: 0 0 8px; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: #1d4ed8; }
  .item { padding: 8px 0; border-top: 1px solid #eceff4; }
  .item:first-of-type { border-top: 0; }
  .item b { font-size: 15px; }
  .meta { font-size: 12.5px; color: #6b7280; }
  .more { display: none; margin: 6px 0 0; font-size: 13.5px; }
  .item.open .more { display: block; }
  button.toggle { margin-top: 4px; padding: 0; border: 0; background: none; color: #1d4ed8; font-size: 13px; cursor: pointer; }
  .chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .chips span { padding: 4px 10px; border-radius: 99px; background: #eef1f6; font-size: 13px; }
  footer { margin-top: 12px; font-size: 12px; color: #6b7280; text-align: center; }
</style>
</head>
<body>
<div class="wrap">
  <header>
    <div class="avatar">TM</div>
    <h1>Taylor Morgan</h1>
    <p class="role">Business student, Northfield College, class of 2027</p>
    <p class="want"><b>Looking for:</b> a summer 2027 marketing or analytics internship.</p>
    <div class="links">
      <a href="mailto:taylor.morgan@example.com">Email me</a>
      <a href="#projects">Projects</a>
      <a href="#skills">Skills</a>
    </div>
  </header>

  <section id="experience">
    <h2>Experience</h2>
    <div class="item">
      <b>Marketing intern</b>
      <div class="meta">Harbor Coffee Co. &middot; Summer 2026</div>
      <button class="toggle" type="button">Show details</button>
      <p class="more">Wrote the weekly customer email and set up a sign-up flyer for the counter. Tracked each email's opens in a spreadsheet and reported the results every Friday.</p>
    </div>
    <div class="item">
      <b>Treasurer</b>
      <div class="meta">Student Marketing Club &middot; 2025 to now</div>
      <button class="toggle" type="button">Show details</button>
      <p class="more">Kept the club budget for four events and posted a one-page summary after each one.</p>
    </div>
  </section>

  <section id="projects">
    <h2>Projects</h2>
    <div class="item">
      <b>Campus coffee survey</b>
      <div class="meta">Course project &middot; 60 responses</div>
      <button class="toggle" type="button">Show details</button>
      <p class="more">Designed a ten-question survey, cleaned the answers in a spreadsheet and presented three findings to the class.</p>
    </div>
  </section>

  <section id="skills">
    <h2>Skills</h2>
    <div class="chips"><span>Spreadsheets</span><span>Survey design</span><span>Basic HTML</span><span>Presenting</span></div>
  </section>

  <footer>Last updated October 2026</footer>
</div>

<script>
  // Each "Show details" button opens the notes under its own item
  document.querySelectorAll('.toggle').forEach((btn) => {
    btn.addEventListener('click', () => {
      const item = btn.closest('.item');
      const open = item.classList.toggle('open');
      btn.textContent = open ? 'Hide details' : 'Show details';
    });
  });
</script>
</body>
</html>
A one-screen resume page. The Show details buttons open the notes under each item.

The details are fictional. Replace them with your own, and keep the "Looking for" line, because it answers the first question a reader has.

Make the page with AI

Any of ChatGPT, Claude or Gemini can write this page. Paste the prompt below, replace the brackets with your facts, and ask for one file.

Write one self-contained HTML file: a one-screen resume page for a
student, laid out for a phone first. Use only inline CSS and a little
JavaScript, with no outside images, fonts or scripts. Use initials in
a coloured circle instead of a photo.

Top: my name, school and graduation year, and one line starting
"Looking for:". Then Experience, Projects and Skills. Each experience
or project shows a title and one line, with a Show details button that
opens two sentences.

My facts: [name, school, year, what you want, 2 jobs or clubs,
1 project, 4 skills]. Do not invent anything I did not give you.

Read the result before you use it. Check every date and name, and make sure nothing was added that you did not do.

Remove any phone number or home address you would not hand to a stranger. Printed codes get scanned by people you have never met.

Paste the finished HTML into a NOS document and choose Create share link. HTML to link walks through it. The page renders as written, and the scripts in it run.

Whoever has the link opens it in a browser, on a phone too, with no account. They can read the page but cannot edit it. If you fix a typo or add a job later, the same link shows the new version.

Keep to one document. A fresh share link is a new address, so a code printed from the old one would still open the old page.

Pictures need care. As of October 2026, NOS blocks images loaded from other sites, so use inline SVG or CSS shapes, as the example does. For a wider look at sending a resume this way, see Share your resume as a link.

Put the code on the paper resume

The sheet below leaves a spot in the top corner. Type your share link, press Make QR code, and the square appears. The Print button opens your browser's print dialog, and a print rule hides the link bar so only the sheet prints.

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>Paper resume with a QR code</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; background: #eef0f4; color: #1f2430; }
  .bar { max-width: 640px; margin: 0 auto 12px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  .bar label { font-size: 13px; font-weight: 600; width: 100%; }
  .bar input { flex: 1 1 220px; min-width: 0; padding: 9px 10px; font-size: 14px; border: 1px solid #b9bfcb; border-radius: 8px; }
  .bar button { padding: 9px 14px; font-size: 14px; border: 0; border-radius: 8px; background: #1d4ed8; color: #fff; cursor: pointer; }
  .msg { font-size: 12.5px; color: #9a3412; width: 100%; min-height: 16px; }
  .sheet { max-width: 640px; margin: 0 auto; background: #fff; padding: 26px 28px; box-shadow: 0 3px 14px rgba(0,0,0,.12); }
  .head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; border-bottom: 2px solid #1f2430; padding-bottom: 12px; }
  h1 { margin: 0; font-size: 26px; }
  .sub { margin: 4px 0 0; font-size: 13px; color: #4b5563; }
  .qr { text-align: center; flex: none; width: 112px; }
  .qr .code { background: #fff; padding: 8px; border: 1px solid #d1d5db; line-height: 0; }
  .qr svg { width: 96px; height: 96px; display: block; }
  .qr .ph { width: 96px; height: 96px; display: grid; place-items: center; font-size: 11px; line-height: 1.3; color: #6b7280; }
  .qr .cap { font-size: 11px; line-height: 1.3; margin-top: 5px; font-weight: 600; }
  h2 { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: #1d4ed8; margin: 16px 0 5px; }
  p, li { font-size: 13px; line-height: 1.5; margin: 0 0 4px; }
  ul { margin: 0; padding-left: 18px; }
  @media (max-width: 480px) { .sheet { padding: 18px 16px; } h1 { font-size: 22px; } .head { flex-direction: column-reverse; align-items: stretch; } .qr { align-self: flex-start; } }
  @media print { body { background: #fff; padding: 0; } .bar { display: none; } .sheet { box-shadow: none; } }
</style>
</head>
<body>
<div class="bar">
  <label for="url">Your resume page link</label>
  <input id="url" type="url" inputmode="url" value="https://example.com/your-resume" autocomplete="off">
  <button id="make" type="button">Make QR code</button>
  <button id="print" type="button">Print</button>
  <div class="msg" id="msg"></div>
</div>

<div class="sheet">
  <div class="head">
    <div>
      <h1>Taylor Morgan</h1>
      <p class="sub">Business student, class of 2027 &middot; Marketing and data analysis</p>
    </div>
    <div class="qr">
      <div class="code" id="code"><div class="ph">Press Make QR code</div></div>
      <div class="cap">Scan for my live resume</div>
    </div>
  </div>
  <h2>Education</h2>
  <p><b>B.A. Business Administration</b>, Northfield College, expected 2027</p>
  <h2>Experience</h2>
  <ul>
    <li><b>Marketing intern</b>, Harbor Coffee Co. (summer 2026): wrote the weekly email, grew the list with a sign-up flyer</li>
    <li><b>Treasurer</b>, Student Marketing Club: kept a budget of a few hundred dollars for four events</li>
  </ul>
  <h2>Skills</h2>
  <p>Spreadsheets, survey design, basic HTML, presenting to small groups</p>
</div>

<script>
  // Minimal QR encoder: byte mode, error correction level M, versions 1-10.
  const EC = { // [ec codewords per block, blocks in group 1, data cw group 1, blocks group 2, data cw group 2] for level M
    1:[10,1,16,0,0], 2:[16,1,28,0,0], 3:[26,1,44,0,0], 4:[18,2,32,0,0], 5:[24,2,43,0,0],
    6:[16,4,27,0,0], 7:[18,4,31,0,0], 8:[22,2,38,2,39], 9:[22,3,36,2,37], 10:[26,4,43,1,44]
  };
  const ALIGN = { 1:[], 2:[6,18], 3:[6,22], 4:[6,26], 5:[6,30], 6:[6,34], 7:[6,22,38], 8:[6,24,42], 9:[6,26,46], 10:[6,28,50] };

  const EXP = [], LOG = [];
  (function () { let x = 1; for (let i = 0; i < 255; i++) { EXP[i] = x; LOG[x] = i; x <<= 1; if (x & 256) x ^= 0x11d; } for (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255]; })();
  const gmul = (a, b) => (a && b) ? EXP[LOG[a] + LOG[b]] : 0;

  function rsRemainder(data, n) {
    let gen = [1];
    for (let i = 0; i < n; i++) {
      const next = new Array(gen.length + 1).fill(0);
      for (let j = 0; j < gen.length; j++) { next[j] ^= gen[j]; next[j + 1] ^= gmul(gen[j], EXP[i]); }
      gen = next;
    }
    const rem = new Array(n).fill(0);
    for (const b of data) {
      const f = b ^ rem.shift(); rem.push(0);
      for (let i = 0; i < n; i++) rem[i] ^= gmul(gen[i + 1], f);
    }
    return rem;
  }

  function bchFormat(data) { let r = data; for (let i = 0; i < 10; i++) r = (r << 1) ^ ((r >>> 9) * 0x537); return ((data << 10) | r) ^ 0x5412; }
  function bchVersion(v) { let r = v; for (let i = 0; i < 12; i++) r = (r << 1) ^ ((r >>> 11) * 0x1f25); return (v << 12) | r; }

  function encodeQR(text) {
    const bytes = Array.from(new TextEncoder().encode(text));
    let ver = 0;
    for (let v = 1; v <= 10; v++) {
      const e = EC[v], cap = e[1] * e[2] + e[3] * e[4];
      const ccBits = v < 10 ? 8 : 16;
      if (4 + ccBits + bytes.length * 8 <= cap * 8) { ver = v; break; }
    }
    if (!ver) return null;
    const e = EC[ver], cap = e[1] * e[2] + e[3] * e[4];
    const bits = [];
    const put = (val, n) => { for (let i = n - 1; i >= 0; i--) bits.push((val >>> i) & 1); };
    put(4, 4); put(bytes.length, ver < 10 ? 8 : 16);
    bytes.forEach(b => put(b, 8));
    put(0, Math.min(4, cap * 8 - bits.length));
    while (bits.length % 8) bits.push(0);
    for (let pad = 0xec; bits.length < cap * 8; pad ^= 0xec ^ 0x11) put(pad, 8);
    const data = [];
    for (let i = 0; i < bits.length; i += 8) data.push(parseInt(bits.slice(i, i + 8).join(''), 2));

    const blocks = []; let pos = 0;
    for (let g = 0; g < 2; g++) for (let k = 0; k < e[1 + g * 2]; k++) { const len = e[2 + g * 2]; blocks.push(data.slice(pos, pos + len)); pos += len; }
    const ecs = blocks.map(b => rsRemainder(b, e[0]));
    const out = [];
    const maxLen = Math.max(...blocks.map(b => b.length));
    for (let i = 0; i < maxLen; i++) blocks.forEach(b => { if (i < b.length) out.push(b[i]); });
    for (let i = 0; i < e[0]; i++) ecs.forEach(c => out.push(c[i]));
    const stream = []; out.forEach(b => put2(stream, b));
    function put2(arr, val) { for (let i = 7; i >= 0; i--) arr.push((val >>> i) & 1); }

    const size = ver * 4 + 17;
    const m = Array.from({ length: size }, () => new Array(size).fill(false));
    const fn = Array.from({ length: size }, () => new Array(size).fill(false));
    const set = (x, y, v) => { m[y][x] = v; fn[y][x] = true; };
    const finder = (cx, cy) => {
      for (let dy = -4; dy <= 4; dy++) for (let dx = -4; dx <= 4; dx++) {
        const x = cx + dx, y = cy + dy; if (x < 0 || y < 0 || x >= size || y >= size) continue;
        const d = Math.max(Math.abs(dx), Math.abs(dy)); set(x, y, d !== 2 && d !== 4);
      }
    };
    finder(3, 3); finder(size - 4, 3); finder(3, size - 4);
    for (let i = 8; i < size - 8; i++) { set(i, 6, i % 2 === 0); set(6, i, i % 2 === 0); }
    const al = ALIGN[ver];
    al.forEach((ay, i) => al.forEach((ax, j) => {
      if ((i === 0 && j === 0) || (i === 0 && j === al.length - 1) || (i === al.length - 1 && j === 0)) return;
      for (let dy = -2; dy <= 2; dy++) for (let dx = -2; dx <= 2; dx++) set(ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
    }));
    set(8, size - 8, true);
    for (let i = 0; i < 9; i++) { fn[8][i] = true; fn[i][8] = true; }
    for (let i = 0; i < 8; i++) { fn[8][size - 1 - i] = true; fn[size - 1 - i][8] = true; }
    if (ver >= 7) {
      const vb = bchVersion(ver);
      for (let i = 0; i < 18; i++) {
        const bit = ((vb >>> i) & 1) === 1, a = size - 11 + (i % 3), b = Math.floor(i / 3);
        set(a, b, bit); set(b, a, bit);
      }
    }

    let k = 0;
    const place = (mask) => {
      const g = m.map(r => r.slice()); k = 0;
      for (let right = size - 1; right >= 1; right -= 2) {
        if (right === 6) right = 5;
        for (let v = 0; v < size; v++) for (let j = 0; j < 2; j++) {
          const x = right - j, up = ((right + 1) & 2) === 0, y = up ? size - 1 - v : v;
          if (fn[y][x]) continue;
          let bit = k < stream.length ? stream[k] === 1 : false; k++;
          const inv = [(x + y) % 2 === 0, y % 2 === 0, x % 3 === 0, (x + y) % 3 === 0, (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0, (x * y) % 2 + (x * y) % 3 === 0, ((x * y) % 2 + (x * y) % 3) % 2 === 0, ((x + y) % 2 + (x * y) % 3) % 2 === 0][mask];
          g[y][x] = bit !== inv;
        }
      }
      const f = bchFormat((0 << 3) | mask); // level M = 00
      const fb = i => ((f >>> i) & 1) === 1;
      for (let i = 0; i <= 5; i++) g[i][8] = fb(i);
      g[7][8] = fb(6); g[8][8] = fb(7); g[8][7] = fb(8);
      for (let i = 9; i < 15; i++) g[8][14 - i] = fb(i);
      for (let i = 0; i < 8; i++) g[8][size - 1 - i] = fb(i);
      for (let i = 8; i < 15; i++) g[size - 15 + i][8] = fb(i);
      g[size - 8][8] = true;
      return g;
    };
    const penalty = (g) => {
      let p = 0;
      for (let t = 0; t < 2; t++) {
        for (let a = 0; a < size; a++) {
          let run = 1;
          for (let b = 1; b < size; b++) {
            const cur = t ? g[b][a] : g[a][b], prev = t ? g[b - 1][a] : g[a][b - 1];
            if (cur === prev) { run++; if (run === 5) p += 3; else if (run > 5) p++; } else run = 1;
          }
        }
      }
      for (let y = 0; y < size - 1; y++) for (let x = 0; x < size - 1; x++) if (g[y][x] === g[y][x + 1] && g[y][x] === g[y + 1][x] && g[y][x] === g[y + 1][x + 1]) p += 3;
      let dark = 0; g.forEach(r => r.forEach(v => { if (v) dark++; }));
      p += Math.floor(Math.abs(dark * 20 - size * size * 10) / (size * size)) * 10;
      return p;
    };
    let best = null, bestP = Infinity;
    for (let mask = 0; mask < 8; mask++) { const g = place(mask); const p = penalty(g); if (p < bestP) { bestP = p; best = g; } }
    return best;
  }

  function toSvg(grid) {
    const n = grid.length, q = 4; // quiet zone: 4 modules
    let d = '';
    grid.forEach((row, y) => row.forEach((v, x) => { if (v) d += 'M' + (x + q) + ' ' + (y + q) + 'h1v1h-1z'; }));
    const t = n + q * 2;
    return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + t + ' ' + t + '" shape-rendering="crispEdges"><rect width="' + t + '" height="' + t + '" fill="#fff"/><path d="' + d + '" fill="#000"/></svg>';
  }

  const url = document.getElementById('url'), msg = document.getElementById('msg'), code = document.getElementById('code');
  function make() {
    const text = url.value.trim();
    msg.textContent = '';
    if (!/^https?:\/\//i.test(text)) { msg.textContent = 'Start the link with https://'; return; }
    const grid = encodeQR(text);
    if (!grid) { msg.textContent = 'That link is too long for this small generator. Use a shorter one.'; return; }
    code.innerHTML = toSvg(grid);
  }
  document.getElementById('make').addEventListener('click', make);
  document.getElementById('print').addEventListener('click', () => window.print());
  url.addEventListener('keydown', e => { if (e.key === 'Enter') make(); });
  make();
</script>
</body>
</html>
A paper resume with a QR slot. Replace the link, press Make QR code, then print.

This small generator runs inside the page, and it needs no account. Its output was decoded with OpenCV's QR reader on a set of sample links during testing. Even so, scan your own printout before you copy a stack.

You can also make the code in Chrome. Chrome Help describes a Create QR code option for the page you have open, with a Download button for the image.

Where it sits in the menu can differ between Chrome versions, so check the Help page for yours.

A code against the paper edge with no label, beside a code in its own white box with a short label.
A code against the paper edge with no label, beside a code in its own white box with a short label.

Three habits help the code scan:

  • Leave a margin. The Denso Wave QR Code site says a margin is required around the code. Put the code in its own white box.
  • Add a few words. "Scan for my live resume" tells a stranger why to raise a phone.
  • Keep the link short. More data means more modules, and a larger code. A shorter link gives a simpler square.

More on sizing and placement is in QR code link.

What the employer does with it

At a table, the employer holds your paper resume. They open the phone's camera, point it at the square, and tap what appears.

Left, a paper resume with fixed text. Right, a phone showing the live page after one scan.
Left, a paper resume with fixed text. Right, a phone showing the live page after one scan.

Apple's support page says the iPhone Camera recognises the code, shows a notification, and opens the link when tapped.

Android's site describes the same: open the camera, point it at the code, and a notification with the details appears. Both are as of October 2026, and settings differ between phones.

You do not have to choose. The sheet can carry all three, and each covers a different reader.

What you print Who it helps The catch
The page's address as text Anyone who types it Each character is typed by hand
A QR code Someone holding a phone with a camera Needs a clean print and a margin
Both, plus your email Every reader Uses a little more space on the sheet
Only the paper text Someone who reads on paper The text is frozen once printed

For a first reply, an email address still does the work. The code is for the person who wants to see more.

When it does not work

What you see Likely cause Fix
The camera shows nothing Code too small, blurred or cut off Print it larger, give it a blank margin, and re-scan
The code opens an old version A new share link was made, not an edit Edit the original document and reprint only if the link changed
The page is tiny and zoomed out The page has no viewport tag Add the viewport meta tag
A picture is missing on the page The image came from another site Use inline SVG or a CSS shape
The code is a dense grid The link is long Use a shorter link and print the code bigger
Only some phones scan it Camera settings differ Print the link as text under the code

Last, test the printed sheet. Scan it with your own phone and with a friend's, in the room's light, from the distance of a table.

Questions people ask

Can a QR code hold my whole resume?

It is the wrong tool for the job. QR codes get larger as they hold more data, so a full resume would need a big, dense square. A link keeps the code small, and the page behind it can be as long as you like.

Can I change my resume after I have printed the code?

Yes, if you edit the same page. The printed code holds the link, and in NOS editing the code of a shared page keeps the same link, so the code shows the new version. A brand-new share link is a different address and needs a new code.

Do I need a QR code generator website?

Not necessarily. Chrome has a Create QR code option for the page you have open, and you can download the image. The example on this page also makes a code from any link you type. Whichever you use, scan the result with a phone before printing.

Should I still put my email on the paper resume?

Yes. The code covers people who scan, and your email covers everyone else. Keep both on the sheet, and put a few words next to the code that say what it opens.

What if the employer's phone does not scan it?

Print the short link in small type under the code. Anyone can then type it into a browser. Keeping the link short also makes the code smaller and easier to read.

Keep reading