avh / contao-faq-structured-bundle
SEO/AEO-optimized FAQ content element with Schema.org FAQPage JSON-LD and three template variants (Handorgel, Bootstrap 5, Tailwind CSS)
Package info
github.com/avhulst/AvhContaoFaqStructuredBundle
Type:contao-bundle
pkg:composer/avh/contao-faq-structured-bundle
Requires
- php: ^8.3
- contao/core-bundle: ^5.7
- mvo/contao-group-widget: ^1.6
README
Package name: avh/contao-faq-structured-bundle
Short description: Custom Contao content element for SEO/AEO-optimized FAQ sections with automatic Schema.org FAQPage JSON-LD output and three template variants (Handorgel, Bootstrap 5, Tailwind CSS).
Target Audiences
- Developers: Technical integration of the bundle into Contao 5.7, template customization, extending FAQ logic.
- Project Managers: Understanding SEO benefits (Rich Results in Google), feature overview for client consulting and project planning.
Core Features
- FAQ Content Element — New content type
faq_structuredin the Texts category. Unlimited question-answer pairs per element. - Schema.org FAQPage JSON-LD — Automatic generation of structured data in FAQPage format. Output in the
<head>of the page. - Three Template Variants — Handorgel (default), Bootstrap 5 Accordion, Tailwind CSS with
<details>element. - Rich-Text Answers — Answers are edited via TinyMCE and support HTML formatting (links, lists, bold text, etc.).
Technical Architecture
Directory Structure
custom/bundles/ContaoFaqStructuredBundle/
├── .editorconfig # Code style configuration
├── .gitignore # Git ignore rules
├── .gitlab-ci.yml # CI/CD pipeline
├── CHANGELOG.md # Changelog
├── LICENSE # MIT license
├── README.md # Documentation (English)
├── README_DE.md # Documentation (German)
├── cliff.toml # git-cliff changelog configuration
├── composer.json # Package definition, dependencies
├── config/
│ └── services.php # Service autowiring
├── contao/
│ ├── dca/
│ │ └── tl_content.php # Backend field definitions (palette, fields)
│ ├── languages/
│ │ ├── de/default.php # German labels
│ │ └── en/default.php # English labels
│ └── templates/twig/
│ ├── .twig-root # Namespace root marker
│ └── content_element/
│ ├── faq_structured.html.twig # Default template (Handorgel)
│ └── faq_structured/
│ ├── bootstrap.html.twig # Bootstrap 5 Accordion
│ └── tailwind.html.twig # Tailwind CSS Details
└── src/
├── ContaoFaqStructuredBundle.php # AbstractBundle (loads services.php)
├── ContaoManager/
│ └── Plugin.php # Bundle registration via BundlePluginInterface
└── Controller/
└── ContentElement/
└── FaqStructuredController.php # Content element logic
Request Flow
- Contao renders the content element
faq_structured FaqStructuredController::getResponse()is called- FAQ entries are deserialized from the serialized
faqItemsfield and filtered - Schema.org FAQPage JSON-LD is built as an array
- Template receives:
faq_items,schema_org_data,accordion_id - JSON-LD is injected into the
<head>via{% do add_schema_org(schema_org_data) %}
Dependencies
| Package | Version | Purpose |
|---|---|---|
php |
^8.3 | Minimum PHP version |
contao/core-bundle |
^5.7 | Contao framework |
mvo/contao-group-widget |
^1.6 | Repeatable field groups in the backend (inputType group) |
Setup / Installation
Requirements:
- Contao Managed Edition 5.7
- PHP 8.3+
mvo/contao-group-widget^1.6 (automatically installed as a dependency)
Installation:
The bundle is located as a local package under custom/bundles/ContaoFaqStructuredBundle/. It is included via the repositories configuration in the project's composer.json.
ddev composer install
After changes to Plugin.php, ddev composer install must be run again — a cache:clear is not sufficient.
Configuration & Usage
Backend Operation
- Create content element: In the article section, create a new content element of type FAQ (Structured Data) (category: Texts).
- Manage questions and answers: In the FAQ Entries section, add any number of question-answer pairs. Answers support rich text (TinyMCE).
- Select template: Use the Custom Template field (
customTpl) to choose one of the three variants. Without selection, the Handorgel template is used.
Available Backend Fields
| Field | Type | Description |
|---|---|---|
faqItems |
Group widget | Repeatable question-answer pairs |
faqQuestion |
Text | Question text (max. 512 characters, required) |
faqAnswer |
Textarea (RTE) | Answer text with TinyMCE editor (required) |
Template Variants
| Variant | File | CSS/JS Dependency | Behavior |
|---|---|---|---|
| Handorgel (default) | faq_structured.html.twig |
contao-components/handorgel (CSS + JS, automatically included) |
First item open, only one item at a time (multiSelectable: false) |
| Bootstrap 5 | faq_structured/bootstrap.html.twig |
Bootstrap 5 must be available in the project | Accordion with data-bs-toggle="collapse", first item open |
| Tailwind CSS | faq_structured/tailwind.html.twig |
Tailwind CSS must be available in the project | Native <details>/<summary> element, no JavaScript, first item open |
Template Selection Guide
- Handorgel: Standalone solution without framework dependency. Suitable for projects without a CSS framework.
- Bootstrap 5: When the project already uses Bootstrap 5. Seamless integration with existing accordion styling.
- Tailwind CSS: Minimal footprint, no JavaScript. Uses native browser behavior (
<details>). Ideal for Tailwind projects.
Schema.org / SEO
The bundle automatically generates FAQPage JSON-LD for each FAQ element. The structure is inserted into the <head> via Contao's add_schema_org() function in the template's metadata block.
Example Output
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is Contao?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Contao is an open-source content management system."
}
}
]
}
Allowed HTML Tags in Answers (JSON-LD)
In the JSON-LD output, answers are sanitized with strip_tags(). The following tags are preserved:
<a>, <b>, <strong>, <i>, <em>, <br>, <p>, <ul>, <ol>, <li>
Google Rich Results
- FAQPage markup enables FAQ Rich Results in Google Search (expandable questions directly in the search result).
- Validation via the Google Rich Results Test Tool.
Important Notes
- Dependency
mvo/contao-group-widget: The bundle uses theinputTypegroupfor repeatable field groups. Without this package, the backend form will not work. - Cache: After changes to FAQ content, the page cache must be refreshed for the updated JSON-LD data to be served.
- Empty entries: Question-answer pairs without a question or answer are automatically filtered out and appear neither in the frontend nor in the JSON-LD.
.twig-rootfile: The filecontao/templates/twig/.twig-rootis mandatory for Contao to correctly recognize the template variants.- Extensibility: Custom template variants can be added as subfolder templates under
contao/templates/twig/content_element/faq_structured/. Alternatively, templates can be overridden in the project undertemplates/.