DEV Community

StudentCalcTools
StudentCalcTools

Posted on

JSON-LD Schema for Calculator Pages

JSON-LD Schema for Calculator Pages

A calculator page is easy for a human to understand. There are inputs, a button, and a result. For a search engine, the same page is harder to read. It sees a form, some headings, and a number that only appears after JavaScript runs. Structured data closes that gap. It tells the search engine, in its own vocabulary, what the page is and what it does.

JSON-LD is the simplest way to add that data. It is a block of JSON inside a script tag. It does not change how the page looks, and it does not interfere with your calculator code. This post shows a practical pattern: one block for the tool itself, one block for the FAQ section, and a checklist for keeping both valid.

Start with the tool, not the result

The most common mistake is trying to mark up the calculated result. Search engines do not need your user's GPA, and you should never put user-entered data into schema. Mark up the tool itself: what it is, what it does, and that it is free to use.

Schema.org has a WebApplication type that fits calculator tools well. Here is a minimal version for a GPA calculator:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebApplication",
  "name": "GPA Calculator",
  "url": "https://www.studentcalctools.com/gpa-calculator/",
  "applicationCategory": "EducationalApplication",
  "operatingSystem": "Any",
  "description": "Calculate GPA on the 4.0 scale using course grades and credit hours.",
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "USD"
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

A few details matter here. The url should be the canonical URL of the page, not a shortened or tracking version. The description should match what the page actually says, not a keyword-stuffed rewrite. And the offers block is only honest if the tool really is free with no sign-up wall in front of the result. If your tool has a paid tier, say so. Schema that contradicts the visible page is worse than no schema at all.

If your calculator runs entirely in the browser, say so. It answers a real user question: does this tool store my grades? Ours does not. Calculations happen in the browser, and nothing is sent to a server.

Add FAQ schema only for visible FAQs

Most calculator pages also have a FAQ section, and that section is a second, separate opportunity. FAQ schema uses the FAQPage type, with one Question and one accepted Answer per item:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "How do you calculate GPA?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Multiply each course grade point value by its credit hours, add the results, then divide by total credits."
      }
    },
    {
      "@type": "Question",
      "name": "Can I use this as a college GPA calculator?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Enter each course, its letter grade, and its credit hours to estimate a semester GPA."
      }
    }
  ]
}
</script>
Enter fullscreen mode Exit fullscreen mode

The rule here is strict: every question in the schema must appear on the visible page, with the same answer. Do not add FAQ schema for questions that only exist in your schema block. A hidden FAQ is not worth the risk.

Keeping the two blocks in separate script tags is deliberate. Each block is easier to validate, remove, and review in a pull request than one giant block mixing the tool, the FAQ, and breadcrumbs.

Generate FAQ schema from one source, not two

The practical problem with FAQ schema is drift. Someone edits the FAQ text on the page and forgets to update the JSON. After a few edits, the schema and the page disagree.

If your site is generated from templates, as ours is, the fix is to generate both from the same data. Keep the FAQ items in one data file or one front matter block, render the visible HTML from it, and render the JSON-LD from the same items. The template below is simplified, but the idea is the important part:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {% for item in page.faq %}
    {
      "@type": "Question",
      "name": {{ item.question | jsonify }},
      "acceptedAnswer": {
        "@type": "Answer",
        "text": {{ item.answer | jsonify }}
      }
    }{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ]
}
</script>
Enter fullscreen mode Exit fullscreen mode

Using a JSON encoder for the question and answer text, instead of pasting raw text into the JSON, avoids the classic breakage: a quote or apostrophe in an answer that silently invalidates the whole block. If your stack is plain HTML rather than a static site generator, the same principle applies in JavaScript. Build the object, then call JSON.stringify on it. Never hand-concatenate JSON strings from user-facing text.

Validate before you publish, every time

Schema is code, so treat it like code. Before a calculator page ships, run its URL through the Rich Results Test and the Schema Markup Validator. The first shows which rich results the page is eligible for; the second checks the structure itself.

Also view the page source. JSON-LD in the HTML source is visible to crawlers without rendering. If your schema only exists after your app boots, a crawler that does not run your JavaScript fully may never see it. Put the script tag in the server-rendered HTML.

Finally, watch for these common mistakes on tool pages:

  • Trailing commas in the JSON, which make the whole block invalid.
  • A ratingValue or review count with no real reviews behind it. Never invent ratings.
  • Marking up a result, a price, or a feature the page does not actually offer.
  • Copying one calculator's schema to another calculator and forgetting to change the name, URL, and description.

That last one is easy to do when you have fourteen tools on one site. A quick search for the old tool name inside the new page's source catches it before publishing.

What schema will not do

Structured data does not rank a page by itself. It helps a search engine understand the page, and it can make the page eligible for richer display. Ranking still comes from the page itself: a tool that works, a clear explanation of the method, honest limitations, and answers to the questions students actually ask.

That is why the order matters. Build the calculator first, write the explanation in plain language, then add schema that describes exactly what you built. On our GPA calculator page, for example, the method is stated in words before any schema describes it: convert each letter grade to grade points, weight by credits, and divide by total credits. The schema repeats that same method in structured form. It adds nothing the page does not already say, and that is the point.

If you maintain calculator tools, start with one page. Add the WebApplication block, add FAQ schema for the FAQs already on that page, validate both blocks, and ship it. Once the pattern is proven on one tool, rolling it out to the rest is a copy of a known-good template.

Top comments (0)