Selling food, beverage, or supplement products online comes with a requirement that most storefront themes ignore completely: an accurate, compliant Nutrition Facts panel on every product. Doing this by hand — pasting an image of a label into each product — does not scale, breaks on mobile, and is invisible to search engines. On a recent project we built a data-driven system that generates the panel automatically from structured nutrient data. Here is how it works under the hood.
The problem with image labels
Storing the panel as an uploaded image seems easy at first, but it fails on every axis that matters:
- It cannot be updated in bulk when a formulation or rounding rule changes.
- It carries no machine-readable data, so it contributes nothing to SEO or structured data.
- It renders poorly on small screens and fails accessibility checks entirely.
The right approach is to store the data and render the label.
Modelling the data
We separated the nutrient facts from their presentation. Each product references a nutrition profile: serving size, servings per container, and a set of nutrient rows (amount per serving, unit, and — where applicable — percent daily value). Storing amounts as raw numbers rather than pre-formatted strings is what makes everything downstream possible.
{
"servingSize": "40 g",
"servingsPerContainer": 12,
"calories": 150,
"nutrients": [
{ "name": "Total Fat", "amount": 8, "unit": "g", "dvBasis": 78 },
{ "name": "Sodium", "amount": 200, "unit": "mg", "dvBasis": 2300 },
{ "name": "Total Carbohydrate", "amount": 15, "unit": "g", "dvBasis": 275 },
{ "name": "Protein", "amount": 5, "unit": "g" }
]
}
The rounding rules are the hard part
Regulators do not let you print raw values. Nutrition labelling has strict rounding and increment rules that differ by nutrient and by magnitude. Sodium above 140 mg rounds to the nearest 10 mg; calories above 50 round to the nearest 10; small amounts round differently again, and some values below a threshold must be declared as zero. We encoded these as a rule set so the label is always compliant regardless of the input:
decimal RoundSodium(decimal mg) =>
mg <= 5 ? 0
: mg <= 140 ? Math.Round(mg / 5) * 5
: Math.Round(mg / 10) * 10;
Percent Daily Value is then computed from the rounded amount against the reference daily intake, and rounded to a whole percent. Centralising these rules in one tested module means a compliance change is a one-line edit, not a project.
Rendering the panel
With clean data and a rounding layer, the visible label becomes a pure rendering concern. We generate semantic HTML that mirrors the official panel layout — the heavy rule under the serving line, the bold nutrient rows, the indented sub-nutrients — styled entirely in CSS. Because it is real markup, it is responsive, screen-reader friendly, and can emit structured data for search engines at the same time.
The payoff
- Every panel is generated from data — not a single hand-built image anywhere in the catalog.
- A formulation or rounding-rule change propagates across the whole catalog from one edit, instead of re-exporting an image for every affected product.
- Labels pass accessibility checks, stay legible on mobile, and carry machine-readable data for search engines.
Takeaway
Whenever a piece of content has rules behind it — nutrition panels, tax tables, shipping matrices — the durable solution is to store the structured data and generate the presentation from it. The rules live in one place, testing is straightforward, and updates stop being a manual chore. If you sell regulated products and your labels are a maintenance headache, we can help you systemise them.