Skip to content
metacraft

Marking page content

MetaCraft can read an entry's rendered page and take structured-data values straight from it. You mark the values in the HTML with two attributes:

  • data-schema-prop="…" marks one value, such as a date, a phone number or a price;
  • data-schema="faq" or data-schema="hours" marks a group of rows, such as FAQs or opening hours.

Marks are exact values. MetaCraft uses them in the JSON-LD without asking, the same way it uses a mapped field, and the editor shows them with a Marked badge. Values it only guesses from ordinary HTML, or that AI suggests, are offered for review instead and are never used until someone accepts them. See Page content and suggestions.

Marks are optional. A page without any is output exactly as before.

When marks are read

MetaCraft reads a page when it captures it. That happens:

  • from the CLI: php system/ee/eecli.php metacraft:capture reads entries changed since their last capture (cron-friendly), with --all, --entry=N or --channel=N for more;
  • on demand from the entry's SEO editor.

Saving an entry only marks its page as changed. It never fetches the page during the save. Page views never capture anything either: the head tag uses the marks stored at the last capture, so a changed mark shows up in the JSON-LD after the next capture.

Where a mark's value goes

Each property has one value. The first source that has it wins:

  1. a value accepted for the entry in the SEO editor;
  2. a mark on the page;
  3. the channel's mapped field;
  4. MetaCraft's automatic value (title, URL, dates, the site's details).

A mark is only used when the entry's schema type has that property. For example, startDate does nothing on a page whose type is Article. Set the type per channel (Settings › Channels) or per entry.

Single values

Put data-schema-prop on the element that holds the value. MetaCraft reads, in order:

The element has Value used
datetime="…" (usually <time>) the attribute
content="…" (usually <meta>) the attribute
an href starting tel: or mailto: the number or address, without the scheme or ?subject=…
<img src="…"> the src
a link with no text the href
anything else its text, with spaces tidied and tags removed

If the same property is marked twice on a page, the first mark wins.

<h1>Spring workshop</h1>
<p>
  <time data-schema-prop="startDate" datetime="2027-03-06T09:00">Saturday 6 March, 9 am</time>
  to <time data-schema-prop="endDate" datetime="2027-03-06T13:00">1 pm</time>
  at <span data-schema-prop="location.name">Main Hall</span>,
  <span data-schema-prop="location.address">4712 Woodrow Bean Dr., El Paso, TX 79924</span>.
</p>
<p>Tickets <span data-schema-prop="offers.price">$25</span>.</p>
<img data-schema-prop="image" src="/images/workshop.jpg" alt="The workshop room">

To give a value that isn't shown on the page, use a <meta> with content:

<meta data-schema-prop="priceRange" content="$$">

Properties you can mark

Property names are the same keys used in channel field mappings. A dotted name fills one part of a nested value.

Type Properties
MedicalWebPage lastReviewed, reviewedBy
FAQPage mainEntity (use a faq group)
Article, BlogPosting author (a name), image
Event startDate, endDate, location.name, location.address, offers.url, image
Product offers.price, brand, image
Service serviceType, areaServed
Person jobTitle, image
Physician medicalSpecialty, telephone, image
LocalBusiness (location) address.streetAddress, address.addressLocality, address.addressRegion, address.postalCode, address.addressCountry, telephone, openingHoursSpecification (use an hours group), geo.latitude, geo.longitude, image

Other names are ignored. Values follow the same rules as mapped fields:

  • Dates: an ISO date or date-time works best (2027-03-06 or 2027-03-06T09:00).
  • Prices: include a currency, for example $25, 25 USD or €19.50. A price without one counts as missing, and a Product without a price is output as a plain WebPage.
  • Images: a full or site-relative URL.

Groups: FAQs and opening hours

Put data-schema="faq" or data-schema="hours" on an element that wraps the rows. Inside it, mark each cell with data-schema-prop set to its role:

Group Roles Fills
faq question, answer mainEntity (FAQPage)
hours day, opens, closes openingHoursSpecification (LocalBusiness)

Rows don't need their own wrapper. A role seen again starts the next row. Several groups of the same kind on one page are joined in page order.

<section data-schema="faq">
  <h3 data-schema-prop="question">Is Deep TMS painful?</h3>
  <p data-schema-prop="answer">Most people feel a light tapping on the scalp.</p>

  <h3 data-schema-prop="question">How long is a session?</h3>
  <div data-schema-prop="answer"><p>About <strong>20 minutes</strong>.</p></div>
</section>
<table data-schema="hours">
  <tr><td data-schema-prop="day">Mon–Fri</td><td data-schema-prop="opens">8:00 AM</td><td data-schema-prop="closes">5:00 PM</td></tr>
  <tr><td data-schema-prop="day">Saturday</td><td data-schema-prop="opens">9 am</td><td data-schema-prop="closes">noon</td></tr>
  <tr><td data-schema-prop="day">Sunday</td><td data-schema-prop="opens">Closed</td></tr>
</table>

Hours are read the same way as a Grid field's:

  • Days: a day (Monday, Mon, Mo), a range (Mon–Fri, Tuesday to Thursday), a list (Sat & Sun), or Daily, Weekdays or Weekends.
  • Times: 12- or 24-hour (8am, 8:30 p.m., 17:30, noon).
  • Closed: Closed in opens.

A row that can't be read is skipped and listed in the editor's Structured data tab. An answer keeps its text only; markup inside it is removed.

Where to put marks

In templates

Add the attributes where the template outputs the field:

<time data-schema-prop="startDate" datetime="{event_start format='%Y-%m-%dT%H:%i'}">{event_start format="%l %j %F"}</time>

If a field is already mapped in the channel's settings, you don't need a mark for it. A mark only helps when the value isn't in a field of its own.

In PageCraft and StoryCraft content

PageCraft and StoryCraft store rendered HTML, so marks inside their blocks are read like any other HTML. Add the attributes in a block's HTML (source) view, and always give them a value: the ContentBox editor underneath drops some empty attributes. Check the saved page's source to confirm a mark survived.

Dynamic sections are rendered from EE templates, so marks go in those templates as above.

Hand-written HTML

Any element can carry a mark: a <span> around text, a <meta> for a hidden value, or the <time>, <a href="tel:…"> and <img> the page already has.

Without marks

Pages without marks still give MetaCraft useful hints. When it captures a page, it also looks for:

  • tel: and mailto: links;
  • an <address> element;
  • <time datetime> dates;
  • <details>/<summary> pairs, and headings ending in ? followed by an answer.

These become suggestions in the editor. They are not used until someone accepts them, because ordinary HTML doesn't say which property a value belongs to. If AI is enabled (Settings › AI), it can suggest further values from the page's text, and those are reviewed the same way.

What MetaCraft reads

  • Only the entry's own public page, fetched from your site with the user agent MetaCraft-Capture/1.3. Closed, future and expired entries aren't fetched.
  • Marks are read from the whole page. Text for suggestions comes from <main> (or <body> when there is no <main>), without navigation, header, footer, scripts, styles or forms.
  • Pages larger than 5 MB, redirects and non-200 responses are not read. The editor shows why.