When I started adding structured data to posts in ICanUp, the simplest approach looked obvious: build a BlogPosting object and pass the title, description, URL, and author name.
It quickly became clear that JSON-LD should not become a separate SEO copy of Post state.
Structured data should describe the same Post entity, the same canonical URL, the same locale, and the same author that the application already exposes.

What I Want BlogPosting to Describe
For me, BlogPosting should answer a few simple questions.
What content is this? Which URL is its canonical public URL? Which language version is currently being shown? Who wrote it? Where is the author entity?
If JSON-LD answers those questions differently from the page itself, the SEO infrastructure already has two sources of truth.
{ "@context": "https://schema.org", "@type": "BlogPosting", "headline": "Localized post title", "description": "Localized post description", "url": "CANONICAL_URL", "author": { "@type": "Person", "name": "Alyona Yakymiv", "url": "AUTHOR_URL" }}The Author Is More Than a String
It is easy to start by putting the author's name into a plain text field.
In ICanUp, however, the author has a public page, a professional identity, and links to external profiles.
That is why my BlogPosting uses a Person object instead of only an author string.
{ "author": { "@type": "Person", "name": "Alyona Yakymiv", "url": "https://icanup.com.ua/author/alyona-yakymiv" }}This creates an explicit relationship between the Post and the author page.
A search engine receives not only a display name but also the URL of an entity that can be connected across Posts and Person structured data.
Image and sameAs Strengthen the Person Entity
For the author page, I also designed the Person data to include an image and sameAs links to professional profiles.
Those values belong to the author entity. They should not be reinvented separately inside every Post.
I prefer one author contract that can be reused by both Person and BlogPosting.
{ "@type": "Person", "name": "Alyona Yakymiv", "url": "https://icanup.com.ua/author/alyona-yakymiv", "image": "AUTHOR_IMAGE_URL", "jobTitle": "Full-stack developer", "sameAs": [ "PROFESSIONAL_PROFILE_URL" ]}The JSON-LD URL Should Not Live Separately from Canonical
The next risk is generating the BlogPosting URL manually.
That becomes especially fragile in a multilingual application where the default locale may be unprefixed while EN uses /en.
The structured URL should come from the same public URL contract that produces the canonical URL.
UK:https://icanup.com.ua/posts/example-slug EN:https://icanup.com.ua/en/posts/example-slugThis is the same principle I use for canonical and hreflang in multilingual Laravel.
An SEO consumer should not independently decide what a public route looks like.
Headline and Description Come from the Current Locale
A shared entity identity does not mean shared Post metadata.
UK and EN can use the same Post ID and shared slug while having different titles, descriptions, and visible content.
BlogPosting therefore needs to serialize metadata from the current localized page.
Field | Shared or localized |
|---|---|
Post identity | Shared |
Slug | Shared |
Author entity | Shared |
Headline | Localized |
Description | Localized |
Canonical URL | Locale-aware |
Visible content | Localized |
A Shared Slug Does Not Mean an Identical BlogPosting
In ICanUp, a Post uses the same shared slug across locales.
The English BlogPosting still needs the English headline, English description, and English canonical URL.
This is another reason I keep route identity separate from content localization.
I covered that shared-slug contract in more detail in my Post about shared slugs vs translated slugs.
The Author Remains the Same Entity Across Locales
Unlike a headline or description, the author does not become a different person when the locale changes.
The Person identity remains shared even when the professional description on the author page is localized.
Localized presentation should not create duplicate real-world Person entities.
A Dedicated Author Page Makes the Relationship Explicit
Before the author page existed, the author name inside a post was mostly a display value.
With a public author page, the Post UI, BlogPosting JSON-LD, and Person structured data can point to the same author entity.
It also removes the need to copy professional author information into every post.
Person and BlogPosting Should Agree
The author page describes a Person.
The BlogPosting author property refers to that same person.
If the name, URL, or other identity fields differ between those structured payloads, the entity graph becomes less consistent.
{ "@context": "https://schema.org", "@type": "Person", "name": "Alyona Yakymiv", "url": "https://icanup.com.ua/author/alyona-yakymiv", "image": "AUTHOR_IMAGE_URL", "sameAs": [ "PROFESSIONAL_PROFILE_URL" ]}JSON-LD Needs to Be in the Initial HTML
Another important part of the implementation is SSR.
I do not want to rely on a crawler executing JavaScript, waiting for hydration, and only then discovering structured data.
BlogPosting JSON-LD should be present directly in the server-rendered HTML.
<script type="application/ld+json">{ "@context": "https://schema.org", "@type": "BlogPosting", "headline": "Localized title", "description": "Localized description", "url": "https://icanup.com.ua/en/posts/example-slug", "author": { "@type": "Person", "name": "Alyona Yakymiv", "url": "https://icanup.com.ua/author/alyona-yakymiv" }}</script>This continues a problem I had already seen before: meta tags were visible in the browser but missing from the initial Inertia SSR HTML.
How I Verify Structured Data
curl -s \ https://icanup.com.ua/en/posts/example-slug \ | grep -A 30 'application/ld+json'Browser developer tools are still useful, but for this test case I care about the actual response HTML.
That confirms that structured data is not being inserted only after client-side rendering.
I Do Not Want a Separate Set of SEO Values for JSON-LD
The worst design for me would be one title for the page, a meta title for SEO, another manually maintained headline for JSON-LD, and yet another hand-built URL.
The more independent copies of public state I have, the easier it becomes for them to drift apart.
Structured data should be assembled from already normalized post, author, and routing data.
What I Would Avoid
Do not represent the author only as plain text when a real author entity already exists.
Do not hardcode the canonical URL inside JSON-LD.
Do not use the UK headline on the EN page.
Do not create a separate slug contract only for structured data.
Do not duplicate author profile data inside every post.
Do not rely only on client-side insertion of JSON-LD.
Do not let Person and BlogPosting use different author URLs.
What I Verify in Tests
BlogPosting JSON-LD is present in the initial post HTML.
The author has type
Person.The author contains the expected name and URL.
The author page contains its own Person JSON-LD.
The localized page uses the localized headline and description.
The structured URL matches the public canonical URL for the current locale.
UK and EN do not mix localized metadata.
The author link from the post points to the same author entity.
For me, good BlogPosting JSON-LD does not invent new facts about a post. It consistently describes the facts the application already treats as true.
What I Kept for Next Time
- Use a Person object instead of an author string when the author entity already exists.
- Keep author identity shared across localized pages.
- Take headline and description from the current locale.
- Take the public URL from the same routing contract used by canonical.
- Do not create separate SEO copies of data only for JSON-LD.
- Render structured data in the initial SSR HTML.
- Verify Person and BlogPosting as one connected entity graph.
Conclusion
In ICanUp, BlogPosting JSON-LD became less of a standalone SEO feature and more of another consumer of existing application contracts.
Localized post metadata comes from the current language version, the URL stays aligned with canonical routing, and the author is represented as a real Person entity with a dedicated public page.
All of this needs to be available in the server-rendered HTML before hydration.
The less independent logic the JSON-LD builder owns, the lower the risk that structured data starts describing a different page from the one the user actually sees.



