I Built a Subway Nutrition Calculator

Published: (April 4, 2026 at 05:16 AM EDT)
6 min read
Source: Dev.to

Source: Dev.to

Introduction

Just a single HTML file with a ton of JavaScript, a massive JSON‑like data structure, and a stubborn refusal to let a bad UI ruin my lunch.

I’m going to walk you through how I built it, what broke along the way, and what I’d do differently next time.

The Problem

You’d have to manually add:

  • bread calories
  • meat calories
  • veggie calories (most are zero, but olives and avocado aren’t)
  • sauce calories

Then double it for a foot‑long, remember that cheese adds fat and sodium, and don’t forget the salt and pepper.

It’s tedious and error‑prone – exactly the kind of problem a simple web tool can solve.

Data Collection

I needed a complete, consistent dataset covering every possible ingredient:

CategoryItems
Breads12
Pre‑made sandwiches30+
Proteins (a la carte)20+
Cheeses5
Vegetables15
Condiments25+ (Normal & Light)
Seasonings3
Sides2
Salads25+
Wraps25+
No‑Bready Bowls25+
Protein Pockets4
Soups3
Desserts7
Sidekicks8
Total≈ 300+

Sources

  1. Subway’s official US nutrition PDF (2026 version)
  2. Their online menu
  3. Third‑party aggregators
  4. Store‑level ingredient sheets

Lesson: Never trust a single source. Cross‑reference everything and be transparent about where the data comes from and what’s estimated.

Data Structure

I created one massive JavaScript object called subwayMenu. It contains arrays for each category (breads, proteins, cheeses, vegetables, condiments, etc.).

Every item follows the same schema:

{
    id: 'artisan-italian-bread-6inch',
    name: '6" Artisan Italian Bread',
    servingSize_g: 71,
    calories: 210,
    totalFat: 2,
    saturatedFat: 1,
    transFat: 0,
    cholesterol: 0,
    sodium: 380,
    totalCarbs: 39,
    dietaryFiber: 1,
    sugars: 3,
    addedSugars: 2,
    protein: 8,
    vitaminA_mcg: 0,
    vitaminC_mg: 0,
    calcium_mg: 1040,
    iron_mg: 16.2,
    category: 'bread'
}

All nutrition fields are present for every item, which makes looping, calculating, and filtering straightforward.

Core Logic

Size Multiplier

// quantity system (simplified)
const sizeMultiplier = isFootlong ? 2 : 1;

calculateTotalNutrition

  1. Initialise a totals object with zeros for every nutrition field.
  2. Apply the size multiplier (2 for foot‑long) to categories that scale with sandwich size.
  3. Loop through every selected category (bread, proteins, cheeses, vegetables, condiments, …) and add the item’s nutrition multiplied by quantity × multiplier.
  4. Return the final totals and a list of ingredients (used for the “ingredients” panel).

Rounding is deferred until the very end, where values are rounded to one decimal place for a clean display.

UI Design

No external libraries – just plain HTML, CSS, and vanilla JavaScript.

AreaDescription
Left sideBuilder with collapsible categories (Bread, Proteins, Cheeses, …) and a tab switcher for menu types (Sandwich, Salads, Wraps, …).
Right sideResults section with a nutrition label, calorie progress bar, current selection list, and action buttons.

Tab System & Collapsible Categories

<!-- simplified markup -->
<div class="category-header" onclick="subwayToggleDropdown(this)">
    <span>Breads</span>
    <svg><!-- arrow icon --></svg>
</div>
<div class="category-items">
    <!-- list of breads -->
</div>

subwayToggleDropdown adds/removes an expanded class and rotates the SVG arrow.

Search Inside Categories

A simple input field filters the visible items in the currently opened category.

“Current Selection” Panel

  • Lists every chosen item with quantity and calories.

  • Each entry has an X button that calls subwayRemoveItemFromSelection, which:

    1. Updates the currentSelection object.
    2. Re‑renders the affected category (to uncheck the item).
    3. Re‑renders the selection panel itself.

This small quality‑of‑life feature prevents the user from having to scroll back up to see what they’ve added.

Exporting Data

function subwaySaveNutritionInfo() {
    const { totals, ingredients } = calculateTotalNutrition();
    const lines = [
        `Meal: ${selectedMenuType}`,
        `Size: ${isFootlong ? 'Foot‑long' : '6‑inch'}`,
        '',
        'Ingredients:',
        ...ingredients.map(i => `- ${i.name} ×${i.qty}`),
        '',
        'Nutrition Facts:',
        `Calories: ${totals.calories} kcal`,
        // …other fields…
    ];
    const blob = new Blob([lines.join('\n')], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'subway-nutrition.txt';
    a.click();
    URL.revokeObjectURL(url);
    // temporary “Saved!” feedback
    showSaveFeedback();
}

Plain‑text export works everywhere and lets users paste the data into notes or spreadsheets.

Known Issues & Gotchas

  1. Bread radio group – only one bread can be selected at a time.
  2. Foot‑long multiplier – must be applied to every size‑dependent ingredient.
  3. Search resetting – changing an item resets the search box; need to preserve the query.
  4. Save button – doesn’t work in some older browsers (requires Blob support).

Takeaways

  • Data first: Clean, consistent data is ~80 % of the work.
  • Logic before UI: Get the calculation engine working in the console before building the interface.
  • User‑centred design: A “Current Selection” panel and easy export make the tool genuinely useful.

Final Thoughts

The code is a little messy and the data could be more complete, but the calculator works. You can build a custom Subway meal, see exactly what you’re eating, and save the results for later.

If you’re a developer thinking about building a similar tool for another restaurant chain, my advice is:

  1. Start with the data.
  2. Build the calculation engine first.
  3. Add UI features that solve real pain points (search, selection list, export).

Happy coding! 🚀

Testing with Real Users

I handed this to a few friends and watched where they got confused. That’s how I learned about the foot‑long multiplier bug.

Keep It Simple

You don’t need a backend, a database, or a build system. A single HTML file with inline CSS and JS is fine for a tool like this.

Practical Use

Now, whenever I walk into Subway, I know exactly what I’m ordering. And if I’m not sure, I open this calculator on my phone and build it before I get to the counter.

The Goal

That’s the whole point—tools should make your life easier, not harder.

Closing Thoughts

I hope this walkthrough helps someone else build something useful. And if you just wanted to use the calculator, well, now you know what’s under the hood.

0 views
Back to Blog

Related posts

Read more »

10 Cool CodePen Demos (March 2026)

2026 F1 Drivers Custom Select using appearance: base-select Chris Bolson crafted one of the most impressive custom selects I've seen. It doesn’t even look like...