Skip to content

Commit d412e2b

Browse files
authored
Add HowTo component for JSON-LD (#1607)
1 parent 28e33e6 commit d412e2b

File tree

12 files changed

+2218
-0
lines changed

12 files changed

+2218
-0
lines changed

.changeset/add-howto-jsonld.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"next-seo": minor
3+
---
4+
5+
Add HowToJsonLd component for structured data support
6+
7+
- New `HowToJsonLd` component following Schema.org HowTo specification
8+
- Support for HowToStep, HowToSection, HowToDirection, and HowToTip types
9+
- HowToSupply and HowToTool for materials and equipment
10+
- Duration properties (prepTime, performTime, totalTime) in ISO 8601 format
11+
- estimatedCost as string or MonetaryAmount object
12+
- yield as string or QuantitativeValue
13+
- Video support via VideoObject

README.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,268 @@ Use these formats for time durations:
631631

632632
[↑ Back to Components](#-components-by-category)
633633

634+
### HowToJsonLd
635+
636+
The `HowToJsonLd` component helps you add structured data for how-to guides and tutorials. This can help your content appear as rich results with step-by-step instructions in search results.
637+
638+
#### Basic Usage
639+
640+
```tsx
641+
import { HowToJsonLd } from "next-seo";
642+
643+
export default function HowToPage() {
644+
return (
645+
<>
646+
<HowToJsonLd
647+
name="How to Change a Flat Tire"
648+
description="Step-by-step instructions for safely changing a flat tire"
649+
image="https://example.com/tire-change.jpg"
650+
totalTime="PT30M"
651+
estimatedCost="$20"
652+
supply={["Spare tire", "Wheel wedges"]}
653+
tool={["Lug wrench", "Jack"]}
654+
step={[
655+
"Turn on hazard lights and apply wheel wedges",
656+
"Remove the hubcap and loosen lug nuts",
657+
"Position jack and raise the vehicle",
658+
"Remove flat tire and mount spare",
659+
"Lower vehicle and tighten lug nuts",
660+
]}
661+
/>
662+
<article>
663+
<h1>How to Change a Flat Tire</h1>
664+
{/* Guide content */}
665+
</article>
666+
</>
667+
);
668+
}
669+
```
670+
671+
#### Advanced Example with Sections and Detailed Steps
672+
673+
```tsx
674+
<HowToJsonLd
675+
name="How to Change a Flat Tire"
676+
description="Complete guide to safely changing a flat tire on the roadside"
677+
image={{
678+
url: "https://example.com/tire-change-guide.jpg",
679+
width: 1200,
680+
height: 800,
681+
}}
682+
estimatedCost={{
683+
currency: "USD",
684+
value: 20,
685+
}}
686+
prepTime="PT5M"
687+
performTime="PT25M"
688+
totalTime="PT30M"
689+
yield="1 changed tire"
690+
tool={[
691+
{
692+
name: "Spare tire",
693+
},
694+
{
695+
name: "Lug wrench",
696+
image: "https://example.com/lug-wrench.jpg",
697+
},
698+
{
699+
name: "Jack",
700+
},
701+
{
702+
name: "Wheel wedges",
703+
image: "https://example.com/wheel-wedges.jpg",
704+
},
705+
]}
706+
supply={[
707+
{
708+
name: "Flares",
709+
image: "https://example.com/flares.jpg",
710+
},
711+
]}
712+
step={[
713+
{
714+
"@type": "HowToSection",
715+
name: "Preparation",
716+
position: 1,
717+
itemListElement: [
718+
{
719+
"@type": "HowToStep",
720+
position: 1,
721+
itemListElement: [
722+
{
723+
"@type": "HowToDirection",
724+
position: 1,
725+
text: "Turn on your hazard lights and set the flares.",
726+
},
727+
{
728+
"@type": "HowToTip",
729+
position: 2,
730+
text: "You're going to need space and want to be visible.",
731+
},
732+
],
733+
},
734+
{
735+
"@type": "HowToStep",
736+
position: 2,
737+
itemListElement: [
738+
{
739+
"@type": "HowToDirection",
740+
position: 1,
741+
text: "Position wheel wedges in front of front tires if rear tire is flat, or behind rear tires if front tire is flat.",
742+
},
743+
{
744+
"@type": "HowToTip",
745+
position: 2,
746+
text: "You don't want the car to move while you're working on it.",
747+
},
748+
],
749+
},
750+
],
751+
},
752+
{
753+
"@type": "HowToSection",
754+
name: "Raise the Car",
755+
position: 2,
756+
itemListElement: [
757+
{
758+
"@type": "HowToStep",
759+
position: 1,
760+
text: "Position the jack underneath the car, next to the flat tire.",
761+
image: "https://example.com/position-jack.jpg",
762+
},
763+
{
764+
"@type": "HowToStep",
765+
position: 2,
766+
text: "Raise the jack until the flat tire is just barely off of the ground.",
767+
},
768+
{
769+
"@type": "HowToStep",
770+
position: 3,
771+
text: "Remove the hubcap and loosen the lug nuts.",
772+
},
773+
],
774+
},
775+
{
776+
"@type": "HowToSection",
777+
name: "Finishing Up",
778+
position: 3,
779+
itemListElement: [
780+
{
781+
"@type": "HowToStep",
782+
position: 1,
783+
text: "Lower the jack and tighten the lug nuts with the wrench.",
784+
},
785+
{
786+
"@type": "HowToStep",
787+
position: 2,
788+
text: "Replace the hubcap.",
789+
},
790+
{
791+
"@type": "HowToStep",
792+
position: 3,
793+
text: "Put the equipment and the flat tire away.",
794+
},
795+
],
796+
},
797+
]}
798+
video={{
799+
name: "How to Change a Tire Video Tutorial",
800+
description: "Watch our mechanic demonstrate the proper technique",
801+
thumbnailUrl: "https://example.com/video-thumb.jpg",
802+
contentUrl: "https://example.com/tire-change-video.mp4",
803+
uploadDate: "2024-01-15T08:00:00+00:00",
804+
duration: "PT8M",
805+
}}
806+
/>
807+
```
808+
809+
#### Props
810+
811+
| Property | Type | Description |
812+
| --------------- | ---------------------------------------------------- | ------------------------------------------------------------------------- |
813+
| `name` | `string` | **Required.** The title of the how-to guide |
814+
| `description` | `string` | A brief description of the guide |
815+
| `image` | `string \| ImageObject` | An image of the completed task or project |
816+
| `estimatedCost` | `string \| MonetaryAmount` | The estimated cost of supplies (e.g., "$20" or MonetaryAmount object) |
817+
| `prepTime` | `string` | ISO 8601 duration for preparation time |
818+
| `performTime` | `string` | ISO 8601 duration for the time to perform the instructions |
819+
| `totalTime` | `string` | ISO 8601 duration for total time (prep + perform) |
820+
| `yield` | `string \| QuantitativeValue` | The result of performing the instructions (e.g., "1 birdhouse") |
821+
| `supply` | `string \| HowToSupply \| (string \| HowToSupply)[]` | Supplies consumed when performing the task |
822+
| `tool` | `string \| HowToTool \| (string \| HowToTool)[]` | Tools used but not consumed |
823+
| `step` | `string \| HowToStep \| HowToSection \| (Step)[]` | The steps to complete the task. Can be simple strings, steps, or sections |
824+
| `video` | `VideoObject` | A video showing how to complete the task |
825+
| `scriptId` | `string` | Custom ID for the script tag |
826+
| `scriptKey` | `string` | Custom key prop for React |
827+
828+
#### Step Types
829+
830+
**HowToStep** - A single step in the guide:
831+
832+
```tsx
833+
{
834+
"@type": "HowToStep",
835+
name: "Step Name", // Optional step title
836+
text: "Step instructions", // The instruction text
837+
url: "https://...", // Optional URL for more details
838+
image: "https://...", // Optional step image
839+
}
840+
```
841+
842+
**HowToSection** - A group of related steps:
843+
844+
```tsx
845+
{
846+
"@type": "HowToSection",
847+
name: "Section Name",
848+
position: 1,
849+
itemListElement: [
850+
{ "@type": "HowToStep", text: "First step" },
851+
{ "@type": "HowToStep", text: "Second step" },
852+
]
853+
}
854+
```
855+
856+
**HowToDirection** and **HowToTip** - For detailed step content:
857+
858+
```tsx
859+
{
860+
"@type": "HowToStep",
861+
itemListElement: [
862+
{
863+
"@type": "HowToDirection",
864+
text: "Do this specific action",
865+
beforeMedia: "https://example.com/before.jpg",
866+
afterMedia: "https://example.com/after.jpg",
867+
},
868+
{
869+
"@type": "HowToTip",
870+
text: "Here's a helpful tip",
871+
}
872+
]
873+
}
874+
```
875+
876+
#### Duration Format (ISO 8601)
877+
878+
Use these formats for time durations:
879+
880+
- `PT15M` - 15 minutes
881+
- `PT1H` - 1 hour
882+
- `PT1H30M` - 1 hour 30 minutes
883+
- `PT2H15M` - 2 hours 15 minutes
884+
885+
#### Best Practices
886+
887+
1. **Clear steps**: Write concise, actionable step instructions
888+
2. **Include images**: Add images for complex steps to improve clarity
889+
3. **Separate sections**: Use HowToSection to group related steps logically
890+
4. **Accurate timing**: Provide realistic time estimates for each phase
891+
5. **List all materials**: Include all supplies and tools needed upfront
892+
6. **Add video**: Video content significantly improves search appearance
893+
894+
[↑ Back to Components](#-components-by-category)
895+
634896
### OrganizationJsonLd
635897

636898
The `OrganizationJsonLd` component helps you add structured data about your organization to improve how it appears in search results and knowledge panels.

0 commit comments

Comments
 (0)