📢 Use this project, contribute to it or open issues to help evolve it using Store Discussion.
The Wordpress Integration app provides a way to bring in blog data from the Wordpress API and create a blog homepage, category pages, and blog post pages on your store, using your existing store header, footer, and styling.
Using your terminal and VTEX IO Toolbelt, log in to the VTEX account you are working on and install the [email protected] app.
In your VTEX account's admin, perform the following actions:
- Access the Apps section and then My Apps.
- Select the Wordpress Integration app box.
- In the Settings section, enter your Wordpress URL. This should be the domain where the Wordpress API endpoint is hosted and Wordpress is administered.
- If your Wordpress installation's API is hosted under a path other than
wp-json/wp/v2/, enter the path in the Wordpress API path field. For example, if thepostsendpoint looks likehttps://example.wordpress.com/index.php?rest_route=/wp/v2/posts, enterindex.php?rest_route=/wp/v2/here. If unsure, leave the field blank. - Enter the Title tag for block homepage which will determine the title tag for the Wordpress portions of your store.
- Enter the Store blog home path which is used to include Wordpress posts in the sitemap and be indexed by search engines. For example, the path to your store's blog is www.my-store.com/blog, you would enter 'blog'.
- Save your changes.
/users or /?author=N queries.
ℹ️ The settings option Create Sitemap Entries will likely not need to be modified. This setting tells the app to create the initial sitemap entries. Once this is done, the app programmatically updates this setting to prevent duplicate sitemap entires.
It is time to create the store pages that will host the blog content. Before performing the following actions, make sure you already are logged into the desired VTEX account and working on a Developer workspace.
- Open your Store Theme app in your code editor.
- Add the
wordpress-integrationapp as apeerDependencyin your theme'smanifest.jsonfile:
"peerDependencies": {
+ "vtex.wordpress-integration": "2.x"
}- In the
store/routes.jsonfile, create paths for the blog's new pages as shown below:
"store.blog-home": {
"path": "/blog"
},
"store.blog-category": {
"path": "/blog/category/:categoryslug_id"
},
"store.blog-post": {
"path": "/blog/post/:slug_id"
},
"store.blog-search-result": {
"path": "/blog/search/:term_id"
}ℹ️ You may change blog in each route to another string of your choosing.
| Blog page | Description |
|---|---|
store.blog-home |
The homepage of your blog. This can also be extended (i.e. store.blog-home#custom) to create additional custom blog pages, if needed. |
store.blog-category |
A listing of blog posts belonging to a particular category, derived from a category slug in the page route. |
store.blog-post |
A detail view of a single blog post, derived from a post slug in the page route. |
store.blog-search-result |
A listing of blog posts matching a search query, derived from a search term in the page route. |
If you wish to display WordPress pages on your store site in addition to posts, you can add a route like the one shown below:
"store.custom#blog-page": {
"path": "/blog/page/:slug"
}In addition to that, you can optionally add :page parameters for URL-controlled pagination. For example:
"store.blog-home": {
"path": "/blog(/page/:page)"
},
"store.blog-category": {
"path": "/blog/category/:categoryslug_id(/page/:page)"
},
"store.blog-search-result": {
"path": "/blog/search/:term_id(/page/:page)"
},If the :page parameter is not provided, pagination can alternatively be controlled by query string, such as /blog/category/example-category?page=2 .
Once the routes are set up, you may populate each blog page with blocks.
The Wordpress Integration app provides the following blocks for your use:
| Block name | Description |
|---|---|
blog-all-posts.wordpress-all-posts |
Paginated list of all blog posts, starting with the most recent. Recommended to be placed on store.blog-home page. |
blog-category-list.wordpress-category-list |
Paginated list of blog posts from a specific category. This must be placed on the store.blog-category page, as the category slug is provided by the parameter in the page route. |
blog-post-details.wordpress-post-details |
Shows the details for a single blog post. This must be placed on the store.blog-post page, as the post slug is provided by the parameter in the page route. |
blog-page-details.wordpress-page-details |
Shows the details for a single blog page. This must be placed on a customized store.blog-home#page page to render the contents via the slug passed through the route parameter. |
blog-latest-posts-preview.wordpress-latest-posts-preview |
Shows teasers for the most recent 3-5 posts (default is 3). |
blog-category-preview.wordpress-category-preview |
Shows teasers for the most recent 3-5 posts from a specific category (default is 3). The category ID must be provided as a prop. |
blog-search.wordpress-search |
Search box that shoppers can use to search blog articles. When submitted, the shopper is redirected to the store.blog-search-list page. |
blog-search-list.wordpress-search-list |
Paginated list of blog post search results. This must be placed on the store.blog-search page, as the search terms are provided by the parameter in the page route. |
search-blog-articles-preview.wordpress |
Shows teasers for the most recent 3-5 posts in the store search results page with a link to the store.blog-search-list page. This can only be placed on the main store.search-result template and uses the same search query as the product search component on that page. |
search-blog-articles-list.wordpress |
An alternative to the search-blog-articles-preview.wordpress block, but instead this block shows a complete paginated list of posts in the search results page. It automatically uses the same search query as the product search component on that page. |
blog-related-products.wordpress-related-products |
A specialized wrapper for a product shelf that can be placed on the store.blog-post page. It must be a child of the blog-post-details.wordpress-post-details block. This allows you to tag Wordpress posts with product reference codes, and the products in question will then be displayed in the shelf. The tags must be in the format prod-[reference code]. For example, if your product had a reference code of VTEX01, the tag should be prod-VTEX01. This block must have product-summary.shelf as a child. |
blog-post-container.wordpress |
A context component that provides data for an individual blog post to its child blocks. This must be placed on the store.blog-post page. |
blog-post-navigation.wordpress |
A component that renders "Previous Article" and "Next Article" buttons. Must be placed as a child of a blog-post-container.wordpress block. |
blog-related-posts.wordpress-related-posts |
Similar to the above, but the reverse: this is a block intended to be placed in the theme's store.product page template which will show teasers for 3-5 blog posts (3 by default) that are tagged with the reference code of the product being viewed. |
blog-breadcrumb.wordpress-breadcrumb |
A breadcrumb component intended to be placed at the top of each blog page. |
blog-search-list.wordpress-category-related-posts |
A block that can be used to display the title and body of one or more posts on a store category or department page. Use case: SEO text for your store's departments. By default, when placed on a store category or department page, the block will attempt to display a WordPress post tagged category-{id}, where {id} is the numeric ID of your VTEX store department. |
| Prop Name | Type | Description | Default value |
|---|---|---|---|
title |
string |
Title to be displayed above the block. | null |
numberOfPosts |
number |
The number of posts to be displayed. | 3 |
tags |
array |
Array of tag IDs allowed. When this property is included, only posts that contain all the listed tags will be displayed. | undefined |
excludeTags |
array |
Array of tag IDs to be excluded. Posts containing any of the excluded tags will not be displayed. | undefined |
excludeCategories |
array |
Array of category IDs to be excluded. Posts containing any of the excluded categories will not be displayed. | undefined |
useTextOverlays |
boolean |
Whether each blog post data (title, category, etc) should be overlaid on the post's featured image (true) or not (false). If false, date of publication and category are shown above the image, title and excerpts are shown below it. |
false |
showCategories |
boolean |
Whether the post category should be shown (true) or not (false). |
true |
showDates |
boolean |
Whether the date of publication should be shown (true) or not (false). |
true |
showAuthors |
boolean |
Whether the post author should be shown (true) or not (false). |
false |
showExcerpts |
boolean |
Whether the post excerpts should be shown (true) or not (false). |
false |
absoluteLinks |
boolean |
Whether the links from each blog post should point to the external blog in a new tab (true) or not (false). |
false |
| Prop Name | Type | Description | Default value |
|---|---|---|---|
category |
number |
The numeric ID of the category in the WordPress system. | 0 |
title |
string |
Title to be displayed above the component. | null |
description |
string |
Subheader to be displayed below the component's title. | null |
customLinkText |
string |
Text to be displayed as a link to the store.blog-category page (according to the given category). |
All [category] Posts > |
customLinkTarget |
string |
If you would like the aforementioned link to direct the user somewhere other than the store.blog-category page, you may enter a different target using this prop. |
undefined |
numberOfPosts |
number |
Number of posts to be displayed. | 3 |
useTextOverlays |
boolean |
Whether each blog post data (title, category, etc) should be overlaid on the post's featured image (true) or not (false). If false, date of publication and category are shown above the image, title and summary are shown below it. |
false |
showDates |
boolean |
Whether the date of publication should be shown (true) or not (false). |
true |
showAuthors |
boolean |
Whether the post author should be shown (true) or not (false). |
false |
showExcerpts |
boolean |
Whether the post excerpts should be shown (true) or not (false). |
false |
absoluteLinks |
boolean |
Whether the links from each blog post should point to the external blog in a new tab (true) or not (false). |
false |
| Prop Name | Type | Description | Default value |
|---|---|---|---|
placeholder |
string |
Component's input placeholder. | Search articles... |
| Prop Name | Type | Description | Default value |
|---|---|---|---|
numberOfPosts |
number |
The number of posts to be displayed. | 3 |
useTextOverlays |
boolean |
Whether each blog post data (title, category, etc) should be overlaid on the post's featured image (true) or not (false). If false, date of publication and category are shown above the image, title and excerpts are shown below it. |
false |
showCategories |
boolean |
Whether the post category should be shown (true) or not (false). |
true |
showDates |
boolean |
Whether the date of publication should be shown (true) or not (false). |
true |
showAuthors |
boolean |
Whether the post author should be shown (true) or not (false). |
false |
showExcerpts |
boolean |
Whether the post excerpts should be shown (true) or not (false). |
false |
absoluteLinks |
boolean |
Whether the links from each blog post should point to the external blog in a new tab (true) or not (false). |
false |
| Prop Name | Type | Description | Default value |
|---|---|---|---|
title |
string |
Title to be displayed above the component. | null |
numberOfPosts |
number |
The number of posts to be displayed. | 3 |
useTextOverlays |
boolean |
Whether each blog post data (title, category, etc) should be overlaid on the post's featured image (true) or not (false). If false, date of publication and category are shown above the image, title and excerpts are shown below it. |
false |
showCategories |
boolean |
Whether the post category should be shown (true) or not (false). |
true |
showDates |
boolean |
Whether the date of publication should be shown (true) or not (false). |
true |
showAuthors |
boolean |
Whether the post author should be shown (true) or not (false). |
false |
showExcerpts |
boolean |
Whether the post excerpts should be shown (true) or not (false). |
false |
absoluteLinks |
boolean |
Whether the links from each blog post should point to the external blog in a new tab (true) or not (false). |
false |
| Prop Name | Description | Type | Default value |
|---|---|---|---|
categoryIdentifier |
You may manually specify the ID to be used in the WordPress tag. For example, if you set this prop to "test", the block will look for a WordPress post tagged "category-test". | String | (empty string) |
numberOfPosts |
number |
The number of posts to be displayed. | 3 |
Blocks that use a paginated list accept some common props that allow customization of the paginated list behavior.
These blocks include:
blog-all-posts.wordpress-all-postsblog-category-list.wordpress-category-listblog-search-list.wordpress-search-listsearch-blog-articles-list.wordpress
| Prop Name | Description | Type | Default value |
|---|---|---|---|
postsPerPage |
Number of posts to be displayed in the paginated list. | Number | 10 |
Starting with version 1.6.0 of this app, blog content from multiple WordPress installations is supported.
To accomplish this, a customdomainslug parameter must be added to your blog routes, and your WordPress blocks must be updated with various props.
ℹ️ This configuration is not required if you only wish to display blog content from a single WordPress domain.
It is time to create the store pages that will host the blog content. Before performing the following actions, make sure you already are logged into the desired VTEX account and working on a Developer workspace.
- In the
store/routes.jsonfile, add thecustomdomainslugparameter to the paths for the blog's new pages. For example:
"store.blog-home": {
"path": "/blog"
},
"store.blog-category": {
"path": "/:customdomainslug/category/:categoryslug_id"
},
"store.blog-post": {
"path": "/:customdomainslug/post/:slug_id"
},
"store.blog-search-result": {
"path": "/:customdomainslug/search/:term_id"
},
"store.custom#blog-page": {
"path": "/:customdomainslug/page/:slug_id"
}blog as in the example, or a different string of your choice. All other routes should use the :customdomainslug dynamic parameter.*
In order to support multiple WordPress installations, you will need to update the theme block's list of props.
First, add the customDomains prop to blocks that use the URL params to load blog data. Namely, they are:
blog-category-list.wordpress-category-listblog-post-details.wordpress-post-detailsblog-post-details.wordpress-page-detailsblog-search-list.wordpress-search-listblog-breadcrumb.wordpress-breadcrumb
The value of the customDomains prop should be a JSON list of domain "slugs" and the domain that each slug represents. The first entry should be your "default" slug and domain.
For example, if you wanted URLs with the slug blog to load content from http://www.blog.com/ and URLs with the slug other-blog to load content from http://www.otherblog.com/, you would give each block a prop like this:
{
"props": {
"customDomains": "{ \"blog\":\"http://www.blog.com/\",\"other-blog\":\"http://www.otherblog.com/\" }"
}
}ℹ️ Make sure to follow the format of the example value, including the brackets and escaped double quotes.
Blocks that do not use URL params should be given a different set of props, namely customDomainSlug and customDomain. These blocks are:
blog-all-posts.wordpress-all-postsblog-latest-posts-preview.wordpress-latest-posts-previewblog-category-preview.wordpress-category-previewblog-search.wordpress-searchsearch-blog-articles-preview.wordpresssearch-blog-articles-list.wordpressblog-related-posts.wordpress-related-postsblog-search-list.wordpress-category-related-posts
customDomainSlug must be provided for all blocks when using multiple WordPress installations. customDomain, in turn, only needs to be provided for blocks that are not using the default WordPress domain from the app settings.
Continuing the example from above, any block that shows content from the "default" WordPress domain should receive the customDomainSlug prop with a value of blog. Blocks that show content from the secondary WordPress domain should receive a customDomainSlug prop with a value of other-blog and a customDomain prop with a value of http://www.other-blog.com/ also. For example:
"store.blog-home": {
"blocks": [
"blog-search.wordpress-search",
"blog-latest-posts-preview.wordpress-latest-posts-preview",
"blog-category-preview.wordpress-category-preview#test",
"blog-all-posts.wordpress-all-posts"
]
},
"blog-search.wordpress-search": {
"props": {
"customDomainSlug": "blog"
}
},
"blog-latest-posts-preview.wordpress-latest-posts-preview": {
"props": {
"title": "Latest Posts",
"useTextOverlays": true,
"customDomainSlug": "blog"
}
},
"blog-category-preview.wordpress-category-preview#test": {
"props": {
"category": 5,
"description": "Description of category",
"useTextOverlays": false,
"customDomain": "http://www.otherblog.com/",
"customDomainSlug": "other-blog"
}
},
"blog-all-posts.wordpress-all-posts": {
"props": {
"customDomainSlug": "blog"
}
},ℹ️ Make sure to follow the format of the example value, including the brackets and escaped double quotes.
To have subcategories included in your blog URLs (/blog/category/subcategory), you will need to update your store's routes.json, as well as the theme's blocks that display category-related URLs.
Add an additional category route including the :categoryslug and :subcategoryslug_id parameter:
"store.blog-category": {
"path": "/blog/category/:categoryslug_id"
},
"store.blog-category#subcategory": {
"path": "/blog/category/:categoryslug/:subcategoryslug_id"
},Declare the subcategory block:
"store.blog-category#subcategory": {
"blocks": [
"blog-category-list.wordpress-category-list"
]
},Add the prop subcategoryUrls with a value of true to the blocks below:
- 'blog-all-posts.wordpress-all-posts'
blog-breadcrumb.wordpress-breadcrumb- 'blog-category-list.wordpress-category-list'
- 'blog-category-preview.wordpress-category-preview'
- 'blog-latest-posts-preview.wordpress-latest-posts-preview'
blog-post-details.wordpress-post-details- 'blog-related-products.wordpress-related-products'
- 'search-blog-articles-list.wordpress'
- 'search-blog-articles-preview.wordpress'
For example:
"blog-all-posts.wordpress-all-posts": {
"props": {
"subcategoryUrls": true
}
},The Yoast SEO plugin provides meta tags and structured data for your WordPress content. If your WordPress installation has this plugin, the app will include this data on your posts or pages.
In order to apply CSS customizations in this and other blocks, follow the instructions given in the recipe on Using CSS Handles for store customization.
| CSS Handles |
|---|
breadcrumbContainer |
breadcrumbHomeLink |
breadcrumbLink |
breadcrumbSeparator |
breadcrumbCurrentPage |
categoryBlockContainer |
categoryBlockTitle |
categoryBlockDescription |
categoryBlockFlex |
categoryBlockFlexItem |
categoryBlockLink |
latestPostsBlockContainer |
latestPostsBlockTitle |
latestPostsBlockFlex |
latestPostsBlockFlexFirstColumnItem |
latestPostsBlockFlexSecondColumn |
latestPostsBlockFlexSecondColumnItem |
latestPostsBlockFlexItem |
listTitle |
listContainer |
listFlex |
listFlexItem |
paginationComponent |
postFlex |
postContainer |
postTitle |
postMeta |
postMetaDate |
postMetaAuthor |
postMetaCategory |
postFeaturedImage |
postFeaturedImageContainer |
postBody |
postCategoryLink |
postChildrenContainer |
postNavigationContainer |
postNavigationFlex |
postNavigationFlexItem |
postNavigationLink |
relatedPostsBlockContainer |
relatedPostsBlockTitle |
relatedPostsBlockFlex |
relatedPostsBlockFlexItem |
searchBlockContainer |
searchListTitle |
searchListContainer |
searchListFlex |
searchListFlexItem |
searchResultBlockContainer |
searchResultBlockTitle |
searchResultBlockFlex |
searchResultBlockFlexItem |
searchResultBlockLink |
teaserAuthor |
teaserCategoryLink |
teaserContainer |
teaserDate |
teaserGradientOverlay |
teaserHeader |
teaserImageContainer |
teaserImage |
teaserBody |
teaserSeparator |
teaserTextOverlay |
teaserTextOverlayTitle |
teaserTextOverlayMeta |
teaserTitle |
teaserTitleLink |
wordpressContentLink |
wordpressRelatedProducts |
Thanks goes to these wonderful people:
This project follows the all-contributors specification. Contributions of any kind welcome!