Schema.org in practice 2026: editorial dark illustration of a Schema.org validator showing 6 types of interconnected entities with JSON-LD.
SEO & GEO 10 min

Schema.org in practice: what I have on my website and why

I show you the 6 schemas I have implemented on landinowebs.com, the mistakes I made at the beginning, and the real JSON-LD you can copy. No theory, just what works in 2026.

Daniel Software Engineer

When I started with Schema.org on my first projects, I made all the mistakes that can be made. I put 12 types of schema on the homepage “just in case”, I put fake data to “complete” the information, I hid the JSON-LD in a display:none so the user wouldn’t see it (Google penalises that), and I tied entities with loose strings instead of with @id. It took me months to understand why my schemas didn’t appear on Google and why the AIs didn’t cite me.

In this post I’m going to show you the 6 schemas I have implemented RIGHT NOW on landinowebs.com, the real JSON-LD you can copy, the mistakes I made along the way, and the validator I use before each deploy. It’s a practical post, not theoretical. If you want the complete guide on what Schema.org is and why it matters, Wikipedia and Schema.org’s official doc explain it better than me. What I give you here is what works in 2026 with own data.


The current setup on landinowebs.com

My website has 6 main schema types, all interconnected with @id so Google understands them as a network of entities and not as loose JSON. I put them in the <head> of each page with an Astro component that generates the JSON-LD dynamically from a centralised data/site.ts file. This allows me to change one piece of data (my phone, my address, a price) and have it update across the whole website at once.

I’ll show you them one by one, with the real JSON-LD as I have it in production. You can copy and adapt.

1. Organization (the root entity)

This is the most important schema. It defines who I am, where I am, how to contact me. It’s the “anchor” to which the other schemas connect via @id.

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://www.landinowebs.com/#organization",
  "name": "Landinowebs",
  "legalName": "Daniel Eduardo Sanz Arilla",
  "alternateName": "Landinowebs - Daniel Sanz",
  "url": "https://www.landinowebs.com",
  "logo": {
    "@type": "ImageObject",
    "url": "https://www.landinowebs.com/icon-512.png",
    "width": "512",
    "height": "512"
  },
  "founder": { "@id": "https://www.landinowebs.com/#person" },
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Avenida Cataluña",
    "addressLocality": "Valencia",
    "addressRegion": "Comunidad Valenciana",
    "postalCode": "46020",
    "addressCountry": "ES"
  },
  "contactPoint": {
    "@type": "ContactPoint",
    "telephone": "+34-649-230-522",
    "contactType": "customer service",
    "availableLanguage": ["English", "Spanish"]
  },
  "sameAs": [
    "https://github.com/landinowebs",
    "https://www.linkedin.com/in/landinowebs"
  ]
}

What I learned with this schema: use legalName with my real name (not “Landinowebs SL” because I haven’t incorporated an SL, I’m self-employed). This matters because Google cross-references the legalName with the one on the invoice I issue, and if they don’t match, it doesn’t connect the Knowledge Graph.

2. Person (me, as founder)

Connects my name with the organisation via worksFor. This allows that when someone searches “Daniel Sanz” on Google, my company appears in the Knowledge Panel.

{
  "@context": "https://schema.org",
  "@type": "Person",
  "@id": "https://www.landinowebs.com/#person",
  "name": "Daniel Sanz",
  "givenName": "Daniel Eduardo",
  "familyName": "Sanz Arilla",
  "jobTitle": "Computer Engineer and freelance web developer",
  "url": "https://www.landinowebs.com",
  "alumniOf": {
    "@type": "CollegeOrUniversity",
    "name": "Universitat Politècnica de València",
    "sameAs": "https://www.upv.es"
  },
  "knowsAbout": [
    "Astro", "TypeScript", "Technical SEO", "GEO",
    "React Native", "Schema.org", "Supabase", "WordPress"
  ],
  "knowsLanguage": ["en-US", "es-ES"],
  "worksFor": { "@id": "https://www.landinowebs.com/#organization" }
}

Mistake I made: in the first version I put knowsAbout with 30 technologies, including “Microsoft Excel” because I use Excel sometimes. Google interprets that as “inflating your profile”, and it penalised my Knowledge Graph. I left it at 8 that I really use daily. The rule is: if you can’t hold a 30-minute conversation on that technology, don’t put it.

3. Service (what I sell, one per service)

Each service I offer has its own Service schema, connected to the organisation via provider.

{
  "@context": "https://schema.org",
  "@type": "Service",
  "@id": "https://www.landinowebs.com/#service-web-medida",
  "name": "Custom website development",
  "description": "Corporate websites and custom landing pages from €350 with technical SEO and GEO included",
  "serviceType": ["Web development", "Technical SEO", "GEO"],
  "provider": { "@id": "https://www.landinowebs.com/#organization" },
  "areaServed": [
    { "@type": "Country", "name": "Spain" },
    { "@type": "Country", "name": "Germany" },
    { "@type": "Country", "name": "Netherlands" },
    { "@type": "Country", "name": "France" }
  ],
  "offers": {
    "@type": "Offer",
    "priceCurrency": "EUR",
    "price": "350",
    "priceValidUntil": "2026-12-31",
    "url": "https://www.landinowebs.com/services/"
  }
}

What I learned: put priceValidUntil with a real date (not “2099-12-31” as I did before). Google uses it to show the price in SERP, and if the date is absurd, it ignores it. I update it every 6 months.

4. WebSite (for the whole homepage)

The WebSite schema with SearchAction is what activates the internal search sitemap in Google (that search box that appears in some Google results directly).

{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "@id": "https://www.landinowebs.com/#website",
  "url": "https://www.landinowebs.com",
  "name": "Landinowebs",
  "inLanguage": ["en-US", "es-ES"],
  "publisher": { "@id": "https://www.landinowebs.com/#organization" }
}

Note: I did NOT put SearchAction because my website doesn’t have an internal search engine (it’s static and doesn’t need it). If you put a SearchAction that points to a search engine that doesn’t exist, Google detects it and lowers your ranking. The rule is: Schema describes what exists, not what you’d like to exist.

5. FAQPage (only where there’s a visible FAQ)

Structured FAQ is what cites me most in ChatGPT and Perplexity. When someone asks an AI “how much does a freelance web developer charge in Spain?”, the answer usually comes directly from my FAQPage.

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "@id": "https://www.landinowebs.com/#faq",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "How much does a website cost in Spain in 2026?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A custom landing page costs from €350, a corporate website from €450, and a full custom website from €600. All prices include technical SEO and GEO. Timelines range from 3-7 days for landings to 2-4 weeks for custom websites."
      }
    },
    {
      "@type": "Question",
      "name": "What does the website price include?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Custom design from scratch (no template), configured domain, complete technical SEO (Schema.org, llms.txt, sitemap, hreflang, Open Graph), GEO so AIs recommend you, Core Web Vitals 95+, perfect responsive, 15 days of small changes included, and deliverable code."
      }
    },
    {
      "@type": "Question",
      "name": "Do you work with clients outside Spain?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, I work remotely with clients in Spain, Germany, Netherlands, France, and other European countries. I accept payments in EUR, USD, GBP, and other major currencies. Communication in English or Spanish."
      }
    }
  ]
}

Trick: the text of each answer is what the AI copies verbatim. That’s why I write it as if it were the direct answer to the question, not as a corporate paragraph. When the AI cites, it cites the text as is.

6. BreadcrumbList (on each non-homepage page)

This schema is what makes Google show the navigation path in the search result (Home > Blog > Schema.org). It improves CTR by 5-15% depending on the sector.

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.landinowebs.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Blog",
      "item": "https://www.landinowebs.com/blog/"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Schema.org in practice",
      "item": "https://www.landinowebs.com/blog/schema-org-for-business-websites/"
    }
  ]
}

The 6 mistakes I made (so you don’t fall into them)

Over 2 years touching Schema.org, these are the mistakes that cost me rankings and citability. I tell them to you so you don’t waste the same time.

Mistake 1: Schema hidden in display:none. My first client asked me to “hide the JSON-LD so the user doesn’t see it”. I said yes (mistake). Google detected it in 3 weeks and dropped our ranking. Solution: the JSON-LD goes in <script type="application/ld+json"> which the browser doesn’t render, so you don’t have to hide it. That’s the correct way.

Mistake 2: Schema with fake data. I put aggregateRating: 4.8 on a project before I had reviews. Google penalised me and it took me 4 months to recover. Solution: if you don’t have real data, don’t put the field. An incomplete schema is better than a fake schema.

Mistake 3: Schema on noindex page. I had a LocalBusiness schema on the “Privacy Policy” page (which was noindex). Totally useless. Google doesn’t even look at it on pages it doesn’t want to index. Solution: schema only on indexable pages.

Mistake 4: Entities without their own @id. My first schema had 3 entities connected with strings: "provider": "Landinowebs" instead of "provider": { "@id": "https://.../#organization" }. Google couldn’t follow the connection. Solution: each entity has its own unique @id, and relationships are made with { "@id": "..." }.

Mistake 5: Schema that says one thing and the website another. A client had the schema saying “Service in Madrid” but the website didn’t mention Madrid anywhere. Google detected it as manipulation and dropped their ranking. Solution: the schema describes what the user sees on the website, not what I’d like the website to be.

Mistake 6: Not validating before deploying. I introduced a schema with a syntax error (an extra comma) and it took me 6 weeks to notice. Solution: now I validate with Google Rich Results Test and Schema Markup Validator before each deploy. It takes 30 seconds and saves months.


How I validate my schemas before each deploy

After the mistakes, now I have a fixed process:

  1. Before commit: I open the JSON-LD locally and run it through the Schema Markup Validator to detect syntax errors. It’s instant.
  2. After deploy: I go to the Google Rich Results Test, paste the URL in production, and check that Google detects all schemas as “eligible for rich results”.
  3. Once a month: I go into Google Search Console > Enhancements and check if there are new warnings. If there are, I fix them in the next sprint.

This process has saved me about 4-5 penalties in the last 2 years. It’s worth it.


How I implement it technically (in Astro)

Since I know some of you will ask, this is what I do in Astro to not duplicate the JSON-LD on each page:

// src/components/SEO.astro
---
import siteData from '../data/site';
const { title, description, pathname, schemaType = 'WebPage' } = Astro.props;

const baseSchema = {
  "@context": "https://schema.org",
  "@type": schemaType,
  // ... rest of base schema
};

const fullSchema = [baseSchema, ...Astro.props.extraSchemas];
---
<script type="application/ld+json" set:html={JSON.stringify(fullSchema)} />

And on each page I import SEO.astro and pass the specific schemas:

---
import SEO from '../components/SEO.astro';
const faqSchema = { /* FAQPage schema */ };
---
<SEO
  title="Schema.org in practice"
  description="What I have on my website"
  schemaType="Article"
  extraSchemas={[faqSchema, breadcrumbSchema]}
/>

If you’re interested in how to do it in WordPress or Webflow, write to me and I’ll send you the equivalent.


How to start on your website (priority order)

If you’re just starting with Schema.org, this is the order I recommend so you don’t get lost:

  1. First: implement Organization + Person (if you’re freelance) on ALL pages. It’s the base. Takes you 30 minutes.
  2. Second: implement BreadcrumbList on each non-homepage page. 1 hour.
  3. Third: implement WebSite on the homepage with your Organization connected. 15 minutes.
  4. Fourth: if you have a visible FAQ on any page, add FAQPage there. 30 minutes.
  5. Fifth: implement Service (one per service) on your services page. 1 hour.
  6. Sixth: if you have real reviews, Review or AggregateRating. Only if you really have them.
  7. Seventh: LocalBusiness only if you have a physical location. DON’T put it if you’re 100% online.

Total: an afternoon of work to have a decent setup. And validate each step with Schema Markup Validator before moving to the next.

If you want me to help you implement it on your website, write to me at landinowebs@gmail.com and we’ll do it together. If we make the website for you, this comes included by default, you don’t have to pay for it separately.

Want to apply this to your project?

If you liked the article and want me to help you with your web, app or SEO, write to me. I reply within 24 hours with a fixed-price quote in writing or a call to scope your case, no commitment.