THE NEW VERSION of this tutorial is available here, done with the new WordPress Theme Builder that was introduced in Pinegrow 4.6.

Converting HTML website to WordPress theme

In this tutorial we'll use Pinegrow to convert a one-page portfolio website into a WordPress theme. In the process we'll cover theme structure, static front pages, custom fields, default & custom loops, using custom PHP code and registering new post types.

Meet Freelancer

We’ll use Freelancer Bootstrap HTML template from StartBootstrap for this tutorial.

The page structure

We need to decide how to structure the theme and its content.

The only part that is repeated are portfolio items in the Portfolio section. That could map well to WordPress loop with posts. But using blog posts as portfolio items is not a good idea. What if in the future we decide to have a blog on our page? In that case the portfolio items would be mixed with blog posts. Although we could differentiate them with tags or categories, there is a better way to go about this:

Using a custom post type for portfolio items.

For the rest of the content the simplest solution is to have an About me page that is set as the site’s front page.

Footer column titles and texts can be stored in the page’s custom meta fields. We could store this information in options or use plugins to manage it, but for now let’s go with meta fields because it’s simple and we don’t need any plugins.

Setting up the page

In WordPress Admin, select or create the page that will be our main About me page and edit it. Set the title and add a featured image. That will be enough for start.

Don’t forget to Publish the page.

In Settings -> Reading set the About page as the Front page of the static page:

Save the settings.

Creating the theme

Download the Freelancer theme and open index.html in Pinegrow. Go to WP panel and Activate WordPress plugin:

Select the Body element in the tree, switch to WP panel and add WordPress site action from the Site section by clicking on the “WordPress site” action name:

Set action properties:

  • Check that this is the master page.
  • Enter theme name (for example “Freelancer”) and slug (a lowercase name of the page without spaces, for example “freelancer”).
  • Create and select the theme’s folder, usually located within wp-content/themes of your WordPress installation. This has to be the path to a folder on your computer (like /users/me/wp/…) , not an url like http://mysite/…
  • Set preview url to the URL address of the local WordPress site from where the theme will be server.

IMPORTANT: HTML file needs to be located in a folder. On Export Pinegrow copies all files in the folder where HTML file is located to the theme folder. This could cause problems if index.html file was located on Desktop or any other folder with lots of files and subfolders.

Now, create the theme by going to WordPress -> Export the theme in the top bar in Pinegrow:

NOTE: If you get a message about missing Pinegrow WordPress plugin you should update Pinegrow to the latest version where the plugin is not used anymore.

Take a look into the exported theme folder (use file explorer or your IDE):

Website resources (css, fonts, etc.) were copied to the theme, functions.php and style.css were created and index.html was converted to index.php.

index.php got some WordPress template tags added to the header and footer, texts were enclosed in the _e translate function, but otherwise the content from the HTML version of the page is still there.

Whenever you save the theme HTML file the corresponding PHP files are also instantly auto-exported. This feature is disabled in the trial version.

header.php and footer.php files are a part of every WordPress theme. They contain the top and bottom parts of theme pages while index.php, single.php and similar, contain the middle part of the page - the content.

Since we have a one-page theme, the theme works well without these files (everything is packed in index.php).

But let’s define header and footer anyway.

In Pinegrow, select the body element (we could select any other element that would enclose the actual content of the page) and add Site content action from the Site section.

Export the theme and notice that header.php and footer.php were added to exported theme files. Index.php also changed. Instead of actual header and footer content it has get_header and get_footer function calls.

Activate the theme

Go to WordPress admin and activate the new theme:

The header

Back in Pinegrow, select the website name in the navbar and add Site name action from the Site section:

Now select Actions -> Preview PHP code (or press CMD / CTRL + P) to show the WordPress PHP code that will be generated for the selected element. The code preview is read-only.

Try changing the Replace property of the Site name action and see how this affects the PHP code:

  • Replace Element will replace the whole A element with the name of the site.
  • Replace Content will replace only the content of the A element.

Replacing Content is what we want here. Set it back to “Content” if you changed it.

In PHP preview code the href of the link is set to “#page-top”. We want to change this with the URL address of our website.

Keep the Brand A element selected and add Href action from Links section to it. Set the Url property of the action to Home url. Href is now set to the URL of the home page:

More than one action can be assigned to a single element and their effects are combined.

Saving the index.html will also export the modified index.php.

Previewing the theme

Now you can open the WordPress website in a browser. You can also use Pinegrow’s WordPress preview window that will auto-reload each time the theme is saved.

Choose WordPress -> Open Preview to open the URL address that is set as the preview url on WordPress site action on the body element.

Notice that the name of the WordPress site is now shown instead of “Start Bootstrap” in the header. That’s the result of the Site name action on that element.

Importing WordPress HTML back to the page

Wouldn’t it be nice if the actual name of the site would also be written in our HTML page?

In Pinegrow, choose Actions -> Import WordPress HTML and see what happens:

Use the shortcut CMD / CTRL + W to import WordPress code for the selected element.

Back to the header…

Add another Site name action to the page title in the header.

Add Site description action from the Site section to the description below the title.

Do Import WordPress HTML on both of them.

That was easy! What about the picture in the header?

We’ll display feature image of our main About page.

Select the image and add The post thumbnail action from Posts section. Keep Replace set to Element because in this case we’ll replace the whole placeholder image with the featured image of the page.

But we have a problem. If you preview the site you’ll notice that the image is not shown.

The reason is that WordPress doesn’t know which post’s or page’s featured image it should show. By default the current post in the loop will be used, but we haven’t yet added the loop.

There are two ways we can solve this:

  • The quick and ugly way: lookup the page id in WordPress Admin and enter it into the Post id property of The post thumbnail action.
  • The proper way: add the loop.

We’ll be proper WordPress developers and take the second approach.

The loop

Even though our static front page will display only one page, we still need The Loop to access the page content, meta values and images.

What to enclose in the loop?

We’ll access the post data in the header (featured image), in the About section (content) and again in the footer (custom fields). To avoid repeating the loop multiple times we need to enclose all these sections into the loop.

Let’s collapse the tree elements and take a look at the page structure:

Now, take a Div from the LIB panel and place it below Navbar in the tree.

Then move header, portfolio, about, contact and footer right one level so that they are inside the newly added div:

Select the div element and add The Loop action from Loops section. Use default parameters.

Save the theme and open the preview:

The featured image is shown, because The post thumbnail is now inside The Loop.

About section

Let’s leave the Portfolio section for later and move down to the About section.

Select the About title (first you’ll have to uncollapse the section#about in the tree) and add The title action from Posts section:

Use Preview PHP code to see what this will do.

You can also do Import WordPress HTML on the title, but note that because of so far unknown reasons, WordPress renders a post and not our selected front page page. This happens when static front page is used. We’ll try to make this work in the future.

The textual content of the About section is split in two columns that are shown side by side on large screens:

That means we can’t just add The content action from Posts section to the content element.

Instead we have to read the content, split it in two columns and place it into two columns. First we need to decide how we’ll mark the column split in the content. Let’s use a simple solution: horizontal line (<hr />) placed inside the content will demark two columns.

In WordPress Admin write the content like this:

This is the perfect job for a simple PHP code block:

//take the content and split it into array
$columns = explode( "<hr />", get_the_content() );
$column_1 = '';
$column_2 = '';
//if at least one column set $column_1 variable
if(count($columns) > 0) {
    $column_1 = $columns[0];
//if the second column is set, put it into the $column_2 variable
if(count($columns) > 1) {
    $column_2 = $columns[1];

To insert the code block select the content row and right-click on the PHP code in Misc section:

Select Insert before Row.

A side note: most actions have a Right click menu that lets you quickly insert the HTML element with the action.

Press CMD / CTRL + H (Actions -> Edit code) to edit the HTML source of the selected PHP block.

Paste in the above PHP code between the <php> tags:

Do not use <?php … ?> tags for PHP code in theme HTML pages. Use PHP code block with <php> … </php> tags instead.

Select the first content column and add PHP code action from Misc section with the left click. Set “Function with parameters” to echo $column_1 and Replace to Content:

Do the same for the second column, using echo $column_2.

Save and preview the theme (make the window wide enough for columns to go side by side):

The button

Let’s turn “Download theme” button into “Download My Resume” button.

Double-click inside the button to edit the label or use Actions -> Edit code to change the label in the code view.

In WordPress Admin, go to Media and upload your resume. Set attachment’s category to resume:

We’ll use the category so that we can tell the resume apart from other attachments.

Back in Pinegrow, select the button’s column and add The Loop action from Loops section to it. We need the loop so that we don’t have to hard code attachment id into the theme.

Enter attachments (or another name) into Custom WP Query name on The Loop. This will switch from the default loop to custom WP_Query loop and lots of query parameters will appear on The Loop action. Set them as shown here:

This will loop through all attachment with the category of resume. For us it will be just one attachment.

Select the Button and add Attachment direct url action from Posts section. Set Replace to Href attribute. Open the PHP preview window to see what this will do:

The contact form

Freelancer HTML template comes with a simple PHP mailer that we can also use with the WordPress theme. The mailer is located in mail/contact_me.php and needs to be configured before you can use it. Just open the contact_me.php file and change the configuration options if required.

All we have to do to use the mailer in the theme is set the Action attribute of the form element in PROP panel:

Pinegrow will automatically resolve URLs in the theme to appropriate WordPress-based URL:

We’ll implement titles and texts as custom fields defined on our main page.

In WordPress Admin add custom fields to the front page as shown here (enable Custom fields in Screen Options if they are not shown):

Back in Pinegrow select the first footer title (uncollapse footer if needed) and add Display meta value action from Posts section. Set Key to footer1_title, the field name we used in WordPress Admin.

Then select the P element below the title and add Display meta value action with the Key set to footer1_text.

In the same way, set field footer2_title to “Around the Web” title in the second column and footer3_title and footer3_text to title and text in the third column.

All we have to do is take care of the social links in the second column. We can find many plugins that can display menus with social icons.

But often, the home-baked simple solutions are the best. Let’s do that. Go back to editing our main page in WordPress Admin and add custom fields for social links:

Don’t forget to save the page after adding fields.

In Pinegrow select the Facebook link and add Set meta value to attr action from Posts section with Key set to social_facebook and Attribute set to href:

Note that for displaying the content we used Display meta value while here we use Set meta value to attr!

What if social_facebook field is not set? We should not display the link. To do that select the List item and add IF get_post_meta conditional action from Conditionals section. Set key to social_facebook:

Now go ahead and do the same for other four social links while I take a break from making screen-shots. Use PHP preview to check if everything is fine and be mindful of field names. And remember, Set meta value goes on the Link, while IF get_post_meta goes on the List item (otherwise empty list items would be shown for empty fields).


Let’s do the copyright statement at the very bottom of the page.

First, select the copyright Column and choose Actions -> Edit code. Enclose the site name and year in spans:

Add Site name action to the first span:

Add PHP code echo date( 'Y' ) to the second span:

Set both Replace to Element so that span tags won’t be exported to the theme. Use PHP preview to check it out.

And the best for the last… Portfolio

We’ll implement portfolio entries as custom post types and loop through them with WP_Query twice:

  • once to display the portfolio section
  • once to construct Bootstrap modals that open when a portfolio thumbnail is clicked in Portfolio section.

First, let’s define the custom post type: portfolio_item.

Select the first portfolio item and add Register post type action from Posts section:

  • Set Post type to portfolio_item
  • Name to Portfolio items
  • Singular name to Portfolio item
  • Make it Public
  • Select all Supports values

Save the theme and check the exported functions.php: register_post_type was added to the file. Notice the code section:

/* Pinegrow generated Custom Post Types Beging */
...Custom post types definitions...
/* Pinegrow generated Custom Post Types End */

Do not edit the code within this section because it is auto-generated by Pinegrow whenever you export the theme. You can safely edit all other parts of the file.

If you check the WordPress Admin you’ll notice that Portfolio items was added to the main menu.

Let’s add the WP_Query loop.

Select the portfolio section and add The loop action from Loops section:

  • Uncheck Show No posts text (we’ll simply omit the section if it is empty)
  • Custom WP Query name is portfolio
  • Custom post type is portfolio_item
  • Post status is Published
  • No pagination (show all) is checked
  • Order by is menu_order

Why did we add the loop on the Section and not on the first portfolio item? By adding the loop to the section we enclosed the whole section with have_posts() conditional statement. That will hide the section in case no portfolio items are found.

Of course, we don’t want the whole section to be repeated for each portfolio item. We have to tell Pinegrow which item inside the loop element should actually be repeated. By default that’s the element with The Loop action, but that’s not what we want now.

Select the first portfolio item and add The loop item action from Loops section. This will set the element that will be repeated in the loop:

if you check the PHP code preview for the loop you’ll notice that while the first portfolio item is repeated in the loop, the other five items are still there, hard-coded in the theme.

Select each of the other 5 columns and add Item placeholder action from Loops section to them. That will prevent them from being exported to the theme.

Now, go back to the first item and select its Image. Add The post thumbnail action from Posts section. Pinegrow will automatically take classes from the placeholder image (img-responsive in this case) and pass them to Class parameter of the action (you can see this in PHP Preview):

Select the enclosing Link (the parent of the image element) and inspect its HTML code. It is a special link that opens a modal with more information about the portfolio item. We have to set hrefs so that they will match the modals:

  • #portfolioModal-POST_ID is a good choice

The best way to do that is by adding PHP code action and setting:

  • Function to echo '#portfolioModal-'.get_the_ID()
  • Replace to Attribute -> href

Save the theme and open it in browser or preview window.

Portfolio section is not shown, as expected, because it is empty.

Go to WordPress Admin and add a couple of Portfolio items. Each item also needs a featured image.

After doing that:

But the modals don’t open because we need to create them first.

Select the first Modal at the bottom of the page and add the same loop as in portfolio section:

  • Uncheck Show No posts text (we’ll simply omit the section if it is empty)
  • Custom WP Query name is portfolio
  • Custom post type is portfolio_item
  • Post status is Published
  • No pagination (show all) is checked
  • Order by is menu_order

Add Item placeholder to all other Modals, except the first one, so that they will not be exported to the theme.

On the page view in Pinegrow Shift + click on the first portfolio item to open its Modal (or, select the modal and check “Show during editing” in PROP panel):

  • Add The title to the H2
  • Add The post thumbnail to the image. Set Class attribute to img-responsive img-centered, like in HTML
  • Add The content on the P element below the image

Notice Client, Date and Service fields at the bottom of the modal? We could implement those with custom fields, just like we did for our main page. This can be your homework.

We also need to set Modal’s id attribute so that links in portfolio section will open them.

Shift + click on the close icon or button to close the modal.

Select the first modal and add The id action from Posts section. Set prefix to portfolioModal. See the PHP preview for result:

Just what we wanted!

Let’s save the theme and take it for a spin.

Congratulations! We did it.

Thanks for going through the whole tutorial, assuming you didn’t just scroll down to here :)

Download the tutorial source files

Where to go next?

Check-out our new in-depth video course about creating WordPress themes with Pinegrow.

Blocks for WordPress is a collection of ready-made WordPress theme components and a very useful resource for learning how to use Pinegrow WordPress theme builder with customizer, loops, custom post types, navigation, galleries and more.