Skip to main content
Back to Blog
5 min read

Schema.org for service businesses: the JSON-LD every portfolio should have

Most developer portfolios ship zero structured data. Here are the 4 JSON-LD blocks that help Google understand your service business — with copy-paste code for Next.js.

performancenext.js

Most developer portfolios have a title tag, maybe a meta description, and zero structured data. Google can read your page, but it doesn't understand your page. It doesn't know you're a service business, what you charge, where you operate, or what past clients said.

JSON-LD fixes that. It's a script block in your <head> that tells search engines exactly what your page is about, in a format they can parse.

Here are the 4 JSON-LD blocks every service portfolio should ship, with Next.js code you can copy.

1. ProfessionalService — who you are

This is the foundation. It tells Google you're a service business, not a blog or an e-commerce store.

// app/layout.tsx or a shared component
const businessSchema = {
  '@context': 'https://schema.org',
  '@type': 'ProfessionalService',
  name: 'Your Name — Full-Stack Developer',
  url: 'https://yoursite.com',
  logo: 'https://yoursite.com/logo.png',
  image: 'https://yoursite.com/og-image.jpg',
  description:
    'Full-stack development for startups. Next.js, React Native, Stripe integration.',
  address: {
    '@type': 'PostalAddress',
    addressLocality: 'Bratislava',
    addressCountry: 'SK',
  },
  geo: {
    '@type': 'GeoCoordinates',
    latitude: 48.1486,
    longitude: 17.1077,
  },
  priceRange: '€€',
  areaServed: {
    '@type': 'GeoCircle',
    geoMidpoint: {
      '@type': 'GeoCoordinates',
      latitude: 48.85,
      longitude: 2.35,
    },
    geoRadius: '3000 km',
  },
  knowsLanguage: ['en', 'ru', 'uk'],
  sameAs: [
    'https://github.com/yourusername',
    'https://linkedin.com/in/yourusername',
  ],
}

Why it matters: Google uses ProfessionalService to populate knowledge panels and local business results. Even if you work remotely, the areaServed field tells Google your market reach.

Common mistake: Using Organization instead of ProfessionalService. Organization is too generic — Google can't infer that you sell services.

2. Service — what you sell

One Service block per service you offer. Be specific — "MVP Development" converts better in search than "Web Development."

const serviceSchema = {
  '@context': 'https://schema.org',
  '@type': 'Service',
  serviceType: 'MVP Development',
  provider: {
    '@type': 'ProfessionalService',
    name: 'Your Name',
    url: 'https://yoursite.com',
  },
  areaServed: 'Europe',
  description:
    'End-to-end MVP development for startups. From scoping to launch in 12 weeks.',
  offers: {
    '@type': 'Offer',
    priceCurrency: 'EUR',
    price: '8000',
    priceSpecification: {
      '@type': 'PriceSpecification',
      minPrice: '8000',
      maxPrice: '25000',
      priceCurrency: 'EUR',
    },
  },
}

Why it matters: Service schema can trigger rich snippets with pricing info in search results. For bottom-of-funnel searches like "MVP developer Europe price," this is high-value real estate.

Tip: If you have multiple services (web app, mobile app, consulting), create separate Service blocks. Don't lump everything into one.

3. Review / AggregateRating — social proof

If you have testimonials on your site, mark them up. Google can display star ratings in search results for service businesses.

const reviewSchema = {
  '@context': 'https://schema.org',
  '@type': 'ProfessionalService',
  name: 'Your Name',
  aggregateRating: {
    '@type': 'AggregateRating',
    ratingValue: '5',
    reviewCount: '12',
    bestRating: '5',
  },
  review: [
    {
      '@type': 'Review',
      author: { '@type': 'Person', name: 'Client Name' },
      datePublished: '2026-03-15',
      reviewBody:
        'Shipped our MVP in 10 weeks. Clean code, clear communication, no surprises.',
      reviewRating: { '@type': 'Rating', ratingValue: '5', bestRating: '5' },
    },
  ],
}

Important: Only mark up real reviews from real clients. Google penalizes fabricated reviews. If you have 3 genuine testimonials, mark up 3 — not 30.

Where to place it: On the page where the testimonials actually appear. Don't add review schema to pages that don't show reviews — Google considers that misleading.

4. Article — for blog posts

If you have a blog (and you should), each post should have Article schema.

const articleSchema = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  description: post.description,
  datePublished: post.date,
  dateModified: post.date,
  author: {
    '@type': 'Person',
    name: 'Your Name',
    url: 'https://yoursite.com',
  },
  publisher: {
    '@type': 'ProfessionalService',
    name: 'Your Name',
    logo: { '@type': 'ImageObject', url: 'https://yoursite.com/logo.png' },
  },
  mainEntityOfPage: {
    '@type': 'WebPage',
    '@id': `https://yoursite.com/blog/${post.slug}`,
  },
}

Why it matters: Article schema helps Google understand content freshness (via dateModified), authorship, and topic. It can trigger rich results with publish dates and author info in search.

How to add JSON-LD in Next.js

In App Router, add a <script> tag in your layout or page:

export default function Layout({ children }) {
  return (
    <html>
      <head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(businessSchema),
          }}
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

For page-specific schema (articles, services), add the script block in the page component, not the layout:

export default function BlogPost({ post }) {
  const articleSchema = {
    /* ... */
  }

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(articleSchema),
        }}
      />
      <article>{/* post content */}</article>
    </>
  )
}

Validation

After adding JSON-LD, validate it:

  1. Google's Rich Results Test — tells you if your schema is eligible for rich results
  2. Schema.org Validator — checks structural correctness
  3. Google Search Console — after indexing, check the "Enhancements" section for schema errors

Run validation after every schema change. A missing comma in JSON-LD silently breaks the entire block — no runtime error, just no structured data.

What not to bother with

Four blocks: ProfessionalService, Service, Review, Article. That's the schema.org investment that pays off for a developer portfolio. Everything else is diminishing returns.


Want structured data set up properly on your site? Let's talk — I ship technical SEO by default on every client project.