Rendering JSON-LD data in NextJS and ReactJS is quite straight-forward. Here are the steps involved.

  1. Organize data in a plain JavaScript Object.
  2. Serialize it using JSON.stringify.
  3. Include the data in the script tag using dangerouslySetInnerHTML.
  4. Optional but recommended - Attach a key.

The first step is creating the JSON Object. I used this in GoRemote which is a job-listing site. Here is the function which creates a job schema given the job object. It returns a simple JavaScript object.

function makeJobSchema(job) {
	const desc = stripHTML(job.description)
	return {
		// schema truncated for brevity
		'@context': 'http://schema.org',
		'@type': 'JobPosting',
		datePosted: job.postedAt,
		description: desc,
		title: job.title,
		image: job.company.logo,
		workHours: 'Flexible',
		validThrough: addDaysToDate(job.postedAt, 60),
		hiringOrganization: {
			'@type': 'Organization',
			name: job.company.name,
			sameAs: job.company.website || null,
			logo: job.company.logo,
		},
	}
}

The next step is to serialize this object using JSON.stringify. Once serialized, we insert it in a script tag using dangerouslySetInnerHTML. Here is how the code looks like.

export default function JobSchema({ job }) {
	return (
		<script
			key={`jobJSON-${job.id}`}
			type='application/ld+json'
			dangerouslySetInnerHTML={{ __html: JSON.stringify(makeJobSchema(job)) }}
		/>
	)
}

Now, you might question, why do we use dangerouslySetInnerHTML? Why not insert the serialized object as a string? Because if we do that, React treats it as HTML and so HTML-escapes the text. The output then looks like this.

<script type="application/ld+json">
	{&quot;@context&quot;:&quot;http:...
</script>

This won’t work with bots that will crawl the page for this metadata. In other words, we don’t want to HTML-escape the text within these script tags. So, that’s why we use dangerouslySetInnerHTML.

Another question would be why do you need to add a key? It’s not needed but it’s a good-to-have to avoid the situation where you have multiple JSON-LD’s for a single entity. A key makes sure that only one such instance is included in the rendered page.

Well, there you have it.

Once you have the page running, you can use ngrok to expose your localhost online and Google’s structured data testing tool to make sure everything works as expected.