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"ordata-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:capturereads entries changed since their last capture (cron-friendly), with--all,--entry=Nor--channel=Nfor 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:
- a value accepted for the entry in the SEO editor;
- a mark on the page;
- the channel's mapped field;
- 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-06or2027-03-06T09:00). - Prices: include a currency, for example
$25,25 USDor€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), orDaily,WeekdaysorWeekends. - Times: 12- or 24-hour (
8am,8:30 p.m.,17:30,noon). - Closed:
Closedin 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:andmailto: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.