Build1 publisher3 min readPublished
A default JSON-LD bundle beats a taxonomy research project on every new site
Richard Lemon published the structured data he ships by default, with one rule that does most of the work: nothing goes in the markup unless a visitor can see it on the page.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Richard Lemon published a technical walkthrough on dev.to of his default Schema.org markup bundle, described as the markup he actually uses and where it lives.
- The author writes that he got tired of treating Schema.org like a research project and stopped doing that, building a small default bundle instead.
- The author describes the drill as: new client, new niche, too many schema types, then you add nothing because it feels endless.
- The bundle is a set of JSON-LD blocks he ships on almost every marketing or SaaS site, plus a couple of variants for blogs and local businesses.
- Rule: JSON-LD only, no microdata and no RDFa; it lives in script type="application/ld+json" blocks.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
Richard Lemon has published on dev.to a walkthrough of the default JSON-LD bundle he ships on almost every marketing or SaaS site, plus variants for blogs and local businesses [1][4]. The markup itself is ordinary; the operating decision behind it is the part worth copying, because he says he stopped treating Schema.org as a research project after noticing where the research led [2].
The failure mode he describes is familiar: new client, new niche, too many candidate schema types, and then you add nothing at all because the job feels endless [3]. A fixed default closes the scope. His bundle is governed by four constraints [5][6][7][8][25]. Two are about vocabulary: JSON-LD only, inside `script type="application/ld+json"`, with no microdata and no RDFa [5], and one primary entity per page, on the grounds that Google can handle more but simplicity wins unless a composite is genuinely needed [6]. The other two are about provenance, and they are the ones that change outcomes. The markup is generated from a build step or CMS fields rather than hand-written per page [7], and the data must exist visually: if the user cannot see it, it does not go in the schema, which he frames as staying out of spam territory [8].
Those two rules together mean the page is the source of truth, not a separate document that drifts. The contactPoint block shows the discipline working: he includes a phone number in the Organization markup only if the site displays one, and deletes the block otherwise [21]. Same logic on the site-wide WebSite entity, which he puts only on the root URL, not every page [13], and only with a `potentialAction` if a real search endpoint exists, with no fake endpoints [14]. He matches the `url` value to the canonical link tag every time [15]. According to Lemon, Google uses this block for sitelinks search boxes [12]. The implementation is a `buildWebsiteSchema(config)` helper fed a site name, a URL and an optional search URL, which the page component calls and stringifies [16].
The main entity is a generic Organization for SaaS, agencies without public shops and online-only services, carrying name, url, logo, sameAs and contactPoint [17][18]. Logo is the same file as the header, absolute and crawlable [19]; sameAs holds only real social profiles, not directory listings [20]. It sits on the homepage next to the WebSite block as a second, separate script [22]. Brick-and-mortar clients get a LocalBusiness subtype instead, such as Restaurant, Physiotherapy or HealthClub [23], and the gym example adds an `@id` anchored to a `#business` fragment, a PostalAddress, GeoCoordinates and openingHoursSpecification [24]. Injection is in the head wherever possible, via a Head component in Next.js or written directly in Astro, Svelte and plain HTML [9], with dynamic data rendered on the server and dropped in as stringified content [10]. No CDNs, no external scripts [11].
Two things to watch if you adopt this. First, the visibility rule creates a coupling that has to be enforced somewhere: remove the phone number from the footer and the contactPoint block has to go with it [21][8], which is a template problem, not an editorial one. Second, the walkthrough as supplied breaks off inside the local-business section, so the blog and local-business variants he references are not fully in evidence [26][4].