Guide · Updated September 2026

Implementing WordPress Custom Post Types for Your Converted Site

To implement WordPress custom post types on a site converted from a live webpage, you'll primarily use the `register_post_type()` function within your theme's `functions.php` file, then create templates to display your custom content. This process allows you to structure unique content types like 'Projects' or 'Testimonials' beyond standard posts and pages, greatly improving content management and flexibility.

Do it yourself in about a minute

Install Themify and get your first conversion free — no credit card.

Chrome browser logoAdd to Chrome — free

Why Custom Post Types Matter for a Converted WordPress Site

When you convert a live webpage into a WordPress theme, you often start with static content that was originally designed for a fixed layout. While WordPress automatically handles standard posts and pages, many modern websites require more specialized content structures. This is where custom post types (CPTs) become indispensable.

Custom post types allow you to move beyond the default 'Post' and 'Page' types, enabling you to create distinct content structures for things like portfolios, services, team members, products, testimonials, or events. Instead of trying to force these unique content pieces into a generic 'Page' format or relying on complex custom fields within standard posts, CPTs provide a clean, organized, and scalable solution. They make your content easier to manage, query, and display consistently across your converted site, ensuring maintainability as your site grows.

For example, if your original site had a dedicated 'Portfolio' section, instead of creating individual WordPress pages for each project and manually adding custom fields for project details, you can define a 'Project' custom post type. Each project then becomes its own entry under this CPT, complete with its own title, content editor, featured image, and specific meta boxes for fields like 'Client Name', 'Completion Date', or 'Technologies Used'. This separation of concerns simplifies content entry for clients or team members and makes developing custom display templates much more straightforward.

Planning Your Custom Post Type Structure

Before you dive into coding, take a moment to outline the custom post types your converted site truly needs. A clear plan saves significant time and effort down the line. Consider what unique content sections existed on the original live site and how they differ from standard blog posts or static pages.

For each potential custom post type, ask yourself the following questions:

Once you have a clear understanding of your requirements, you can proceed to the implementation phase. It's often helpful to sketch out how the editing screen for each CPT would look in the WordPress admin, considering what fields and functionalities would be most useful for content creators.

  • **What is the core purpose of this content?** (e.g., displaying individual portfolio items, listing team members, showcasing products).
  • **What are the key pieces of information (metadata) associated with each item?** (e.g., for a 'Service', this might be 'Service Icon', 'Short Description', 'Benefits List'). These will likely become custom fields or custom taxonomies.
  • **Will this content need categories or tags separate from blog posts?** (e.g., 'Project Types' for a 'Project' CPT). This indicates a need for custom taxonomies.
  • **How will this content be displayed on the front end?** (e.g., a single page for each item, an archive page listing all items, or both).
  • **Who will be managing this content?** This influences the labels and user interface options you choose.

Registering Custom Post Types via `functions.php`

The primary method for adding custom post types to your WordPress theme is by using the `register_post_type()` function. This function should be called within your theme's `functions.php` file, ideally hooked into the `init` action. This ensures that the post type is registered early in the WordPress loading process.

Before making any modifications, it's always best practice to create a child theme. If your site was converted into a theme using a tool like Themify, you already have a `functions.php` file ready to modify. Directly editing a parent theme means your changes will be lost upon theme updates. If you haven't created one, do so now to protect your work.

Here's a basic example of how to register a 'Project' custom post type. Place this code within your `functions.php` file. Remember that a converted theme often brings its own set of functions, so ensure your new code is neatly organized and does not conflict with existing functions.

  1. **Open your theme's `functions.php`:** Navigate to `Appearance → Theme File Editor` in your WordPress admin, then select `Theme Functions (functions.php)`. Alternatively, use an FTP client or file manager to access `/wp-content/themes/your-theme-name/functions.php`.
  2. **Add the `register_project_post_type` function:**
  3. ```php function themify_register_project_cpt() { $labels = array( 'name' => _x( 'Projects', 'Post Type General Name', 'textdomain' ), 'singular_name' => _x( 'Project', 'Post Type Singular Name', 'textdomain' ), 'menu_name' => __( 'Projects', 'textdomain' ), 'name_admin_bar' => __( 'Project', 'textdomain' ), 'archives' => __( 'Project Archives', 'textdomain' ), 'attributes' => __( 'Project Attributes', 'textdomain' ), 'parent_item_colon' => __( 'Parent Project:', 'textdomain' ), 'all_items' => __( 'All Projects', 'textdomain' ), 'add_new_item' => __( 'Add New Project', 'textdomain' ), 'add_new' => __( 'Add New', 'textdomain' ), 'new_item' => __( 'New Project', 'textdomain' ), 'edit_item' => __( 'Edit Project', 'textdomain' ), 'update_item' => __( 'Update Project', 'textdomain' ), 'view_item' => __( 'View Project', 'textdomain' ), 'view_items' => __( 'View Projects', 'textdomain' ), 'search_items' => __( 'Search Projects', 'textdomain' ), 'not_found' => __( 'No projects found', 'textdomain' ), 'not_found_in_trash' => __( 'No projects found in Trash', 'textdomain' ), 'featured_image' => __( 'Project Featured Image', 'textdomain' ), 'set_featured_image' => __( 'Set featured image', 'textdomain' ), 'remove_featured_image' => __( 'Remove featured image', 'textdomain' ), 'use_featured_image' => __( 'Use as featured image', 'textdomain' ), 'insert_into_item' => __( 'Insert into project', 'textdomain' ), 'uploaded_to_this_item' => __( 'Uploaded to this project', 'textdomain' ), 'items_list' => __( 'Projects list', 'textdomain' ), 'items_list_navigation' => __( 'Projects list navigation', 'textdomain' ), 'filter_items_list' => __( 'Filter projects list', 'textdomain' ), ); $args = array( 'label' => __( 'Project', 'textdomain' ), 'description' => __( 'Portfolio projects', 'textdomain' ), 'labels' => $labels, 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' ), 'taxonomies' => array( 'category', 'post_tag' ), // Can also add custom taxonomies here 'hierarchical' => false, 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 5, 'menu_icon' => 'dashicons-portfolio', 'show_in_admin_bar' => true, 'show_in_nav_menus' => true, 'can_export' => true, 'has_archive' => true, 'exclude_from_search' => false, 'publicly_queryable' => true, 'capability_type' => 'post', 'show_in_rest' => true, // Enables Gutenberg editor and REST API access ); register_post_type( 'project', $args ); } add_action( 'init', 'themify_register_project_cpt' ); ```
  4. **Save the file.** Once saved, refresh your WordPress admin, and you should see a new 'Projects' menu item in the left-hand sidebar.
  5. **Flush permalinks:** After adding a new custom post type, it's crucial to flush your permalinks. Go to `Settings → Permalinks` in your WordPress admin and simply click `Save Changes` without altering any settings. This updates WordPress's rewrite rules to recognize your new post type's URLs.

Creating Templates for Custom Post Type Display

Once your custom post types are registered, the next step is to create templates to control how they appear on your site. WordPress uses a template hierarchy to determine which file to use for displaying content. For custom post types, this typically involves creating an archive template (to list all items) and a single template (to display an individual item).

Since your theme was converted from a live webpage, it likely already has `index.php`, `archive.php`, and `single.php` files. You can leverage these as starting points, adapting them for your CPTs.

For a 'project' CPT, you would create `archive-project.php` and `single-project.php`.

**1. `archive-project.php` (for listing all projects):**

Create a new file named `archive-project.php` in your theme's root directory. The simplest approach is to copy the content of your theme's existing `archive.php` or `index.php` into this new file. Then, modify the WordPress Loop to specifically query for your 'project' post type, if needed, though WordPress will usually handle this automatically if the file is named correctly. You might adjust the layout to feature project titles, featured images, and short excerpts.

A typical `archive-project.php` might look like this (simplified):

```php <?php get_header(); ?> <div id="primary" class="content-area"> <main id="main" class="site-main"> <?php if ( have_posts() ) : ?> <header class="page-header"> <h1 class="page-title"><?php post_type_archive_title(); ?></h1> </header><!-- .page-header --> <?php // Start the Loop. while ( have_posts() ) : the_post(); // Include the CPT content template part. get_template_part( 'template-parts/content', 'project-archive' ); endwhile; the_posts_navigation(); else : get_template_part( 'template-parts/content', 'none' ); endif; ?> </main><!-- #main --> </div><!-- #primary --> <?php get_sidebar(); ?> <?php get_footer(); ?> ```

You might also create a `template-parts/content-project-archive.php` to define how each individual project is displayed within the archive loop. This allows for cleaner separation.

**2. `single-project.php` (for displaying an individual project):**

Create `single-project.php` in your theme's root. Copy the contents of your theme's `single.php` file into it. This template will display the full content of a single project. You'll likely want to customize the layout to show specific custom fields you've added (e.g., 'Client Name', 'Technologies Used') in a structured way. If you're using a plugin like Advanced Custom Fields (ACF) to manage custom fields, you'd use `the_field('your_field_name')` to display them.

A simplified `single-project.php` could be:

```php <?php get_header(); ?> <div id="primary" class="content-area"> <main id="main" class="site-main"> <?php while ( have_posts() ) : the_post(); get_template_part( 'template-parts/content', 'project-single' ); // If comments are open or we have at least one comment, load up the comment template. if ( comments_open() || get_comments_number() ) { comments_template(); } the_post_navigation(); endwhile; // End of the loop. ?> </main><!-- #main --> </div><!-- #primary --> <?php get_sidebar(); ?> <?php get_footer(); ?> ```

Again, `template-parts/content-project-single.php` would contain the specific layout for a single project, including `the_title()`, `the_content()`, `the_post_thumbnail()`, and any custom fields.

Remember to access these files via FTP/SFTP or your hosting provider's file manager for reliable editing, or use the `Theme File Editor` under `Appearance` in WordPress if you're comfortable with direct edits.

Adding Custom Taxonomies and Custom Fields

Beyond the basic content and title, most custom post types benefit greatly from custom taxonomies and custom fields. These allow for richer content categorization and storage of specific metadata, enhancing content structure and making it easier to query and display information.

**Custom Taxonomies:**

Custom taxonomies are like custom categories or tags for your custom post types. For our 'Project' CPT, we might want 'Project Types' (e.g., 'Web Design', 'Branding', 'Mobile App') or 'Client Industries' (e.g., 'Healthcare', 'Tech', 'Retail').

To register a custom taxonomy, use `register_taxonomy()` in your `functions.php`, typically alongside your CPT registration, hooked into the `init` action:

```php function themify_register_project_taxonomies() { $labels = array( 'name' => _x( 'Project Types', 'taxonomy general name', 'textdomain' ), 'singular_name' => _x( 'Project Type', 'taxonomy singular name', 'textdomain' ), 'search_items' => __( 'Search Project Types', 'textdomain' ), 'all_items' => __( 'All Project Types', 'textdomain' ), 'parent_item' => __( 'Parent Project Type', 'textdomain' ), 'parent_item_colon' => __( 'Parent Project Type:', 'textdomain' ), 'edit_item' => __( 'Edit Project Type', 'textdomain' ), 'update_item' => __( 'Update Project Type', 'textdomain' ), 'add_new_item' => __( 'Add New Project Type', 'textdomain' ), 'new_item_name' => __( 'New Project Type Name', 'textdomain' ), 'menu_name' => __( 'Project Types', 'textdomain' ), ); $args = array( 'hierarchical' => true, // Set to false for a tag-like taxonomy 'labels' => $labels, 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array( 'slug' => 'project-type' ), 'show_in_rest' => true, ); register_taxonomy( 'project_type', array( 'project' ), $args ); // 'project' is the CPT slug } add_action( 'init', 'themify_register_project_taxonomies', 0 ); ```

After saving `functions.php` and refreshing permalinks, you'll see 'Project Types' appear under your 'Projects' menu in the admin.

**Custom Fields:**

Custom fields are used to store unique data points for each item within a CPT. While you can use WordPress's built-in custom fields meta box (which you enable in the 'Screen Options' tab when editing a post), this is often too basic for complex data.

For a better user experience and more robust field types, a plugin like **Advanced Custom Fields (ACF)** is highly recommended. It allows you to create field groups with various field types (text, image, date, URL, etc.) and assign them to specific custom post types. This is generally preferred over manual coding for custom fields, especially for those managing content.

Steps for using ACF (example for a 'Project URL' field):

**1. Install and Activate ACF:** Go to `Plugins → Add New`, search for 'Advanced Custom Fields', install, and activate it.

**2. Create a Field Group:** Navigate to `ACF → Field Groups → Add New` in your WordPress admin.

**3. Add Fields:** Click '+ Add Field', set 'Field Label' to 'Project URL', 'Field Name' to `project_url` (lowercase, no spaces), 'Field Type' to 'URL'.

**4. Set Location Rule:** Under 'Location Rules', set 'Post Type is equal to Project'.

**5. Publish:** Save your Field Group. Now, when you add or edit a 'Project', you'll see your 'Project URL' field.

**6. Display in Template:** In your `single-project.php` or `content-project-single.php` template, you can display this field using:

```php <?php if( get_field('project_url') ): ?> <p>Project Link: <a href="<?php the_field('project_url'); ?>">View Project</a></p> <?php endif; ?> ```

Querying Custom Post Types on Your Converted Site

Once you have custom post types and content, you'll likely want to display them on various parts of your converted site, not just their dedicated archive pages. This involves using `WP_Query` to fetch specific CPT items.

For example, to display the three most recent 'Projects' on your homepage, you would add a new `WP_Query` instance to your `front-page.php` or `home.php` template (or `index.php` if you don't have specific front page templates).

```php <?php // Custom Query for Projects $args = array( 'post_type' => 'project', // Your custom post type slug 'posts_per_page' => 3, // Number of items to display 'post_status' => 'publish', // Only published items 'orderby' => 'date', // Order by date 'order' => 'DESC', // Newest first ); $project_query = new WP_Query( $args ); // Check if the query returned any posts if ( $project_query->have_posts() ) : ?> <section class="themify-latest-projects"> <h2>Our Latest Projects</h2> <div class="project-grid"> <?php // Start the Loop while ( $project_query->have_posts() ) : $project_query->the_post(); // Get template part for project display // Make sure 'content-project-teaser.php' exists in your theme's template-parts folder get_template_part( 'template-parts/content', 'project-teaser' ); endwhile; ?> </div> </section> <?php // Restore original Post Data wp_reset_postdata(); else : // No projects found ?> <p>No projects to display.</p> <?php endif; ?> ```

In this example, `template-parts/content-project-teaser.php` would contain the HTML structure for a single project item in a grid or list, typically showing the title, featured image, and a short excerpt. By using `get_template_part()`, you keep your main template files clean and reusable.

You can also query by custom taxonomies. To display 'Projects' tagged with 'Web Design':

```php $args = array( 'post_type' => 'project', 'posts_per_page' => -1, // Display all projects 'tax_query' => array( array( 'taxonomy' => 'project_type', // Your custom taxonomy slug 'field' => 'slug', 'terms' => 'web-design', // The slug of the term you want to query ), ), ); $web_design_projects = new WP_Query( $args ); // ... then loop through $web_design_projects as shown above ... ```

These queries can be placed anywhere in your theme templates where you want to dynamically pull in CPT content. Remember to always include `wp_reset_postdata();` after a custom `WP_Query` loop to restore the global `$post` object to its original state, preventing unexpected behavior with other WordPress functions.

Integrating Custom Post Types with Navigation Menus

Once your custom post types are created and populated, you'll want to make them accessible through your site's navigation. WordPress provides straightforward ways to add individual CPT items or CPT archive pages to your menus.

Follow these steps in your WordPress admin:

**1. Access Menu Settings:** Navigate to `Appearance → Menus`.

**2. Select or Create a Menu:** Choose an existing menu to edit, or create a new one.

**3. Add Custom Post Type Archive:** On the left side of the screen, you should see a section for your custom post type, for instance, 'Projects'. If you expand this, you'll find an option like 'View All' or 'Projects Archive'. Check this box and click 'Add to Menu'. This adds a link to the archive page (e.g., `yoursite.com/project/`) that lists all items of that CPT.

**4. Add Individual CPT Items:** Also under the 'Projects' section, you will see a list of your individual project entries. You can select specific projects to add them directly to your menu, linking to their single item pages.

**5. Add Custom Taxonomy Archives:** If you created custom taxonomies (e.g., 'Project Types'), you can also add these. Look for a section named 'Project Types' (or whatever you named your taxonomy). Expand it, select specific terms (e.g., 'Web Design'), and add them to your menu. This will link to an archive page showing only projects within that specific category (e.g., `yoursite.com/project-type/web-design/`).

**6. Arrange and Save:** Drag and drop menu items to reorder them or create sub-menus. Once satisfied, click 'Save Menu'.

By integrating your CPTs into navigation, you make your specialized content easily discoverable by visitors, enhancing the user experience on your converted site.

Common Pitfalls and Troubleshooting

Implementing custom post types can sometimes hit snags, especially on a site that's been converted. Here are common issues and how to troubleshoot them:

**1. New CPT Not Appearing in Admin Menu:**

**2. 404 Errors on CPT Single or Archive Pages:**

**3. Custom Fields Not Displaying:**

**4. CPT Content Not Appearing in Search Results:**

**5. Theme Conflicts:**

Regularly backing up your `functions.php` file and the entire theme folder before making changes is always a smart practice. If you encounter persistent issues, temporarily deactivating other plugins can help isolate conflicts, and inspecting your site's error logs can provide valuable clues.

  • **Cause:** Incorrect `register_post_type` arguments or forgotten `add_action` hook.
  • **Solution:** Double-check your `functions.php` file for typos. Ensure the `add_action( 'init', 'your_function_name' );` line is present and correct.
  • **Cause:** WordPress's rewrite rules haven't been updated.
  • **Solution:** Go to `Settings → Permalinks` in your WordPress admin and simply click the 'Save Changes' button. This flushes the rewrite rules. Do this every time you add or significantly modify a CPT or custom taxonomy.
  • **Cause:** Incorrect function calls in your template, or fields not assigned to the correct CPT.
  • **Solution:** For ACF, verify the Field Group's location rules match your CPT. Ensure you're using `get_field('your_field_name')` or `the_field('your_field_name')` with the correct field name (slug) in your `single-yourcpt.php` template.
  • **Cause:** The `exclude_from_search` argument was set to `true` during registration.
  • **Solution:** In `register_post_type()`, ensure `exclude_from_search` is set to `false` and `publicly_queryable` is `true`. Re-flush permalinks.
  • **Cause:** Another plugin or your theme's existing code might be using the same post type slug or conflicting with CPT registration.
  • **Solution:** Choose unique slugs for your CPTs and taxonomies. If using Themify, the generated theme should be clean, but third-party plugins can introduce conflicts. Test by temporarily deactivating plugins one by one if you suspect a conflict.

Frequently asked questions

Can I convert an existing custom post type from an old WordPress site to a new one?
Yes, you can export custom post types (and their content) using WordPress's built-in `Tools → Export` feature, selecting specific post types. Then, on your new converted site, use `Tools → Import` to bring them over. Remember to register the custom post types in your new theme's `functions.php` file *before* importing the content, otherwise, the content might import as standard posts or pages.
What's the difference between `register_post_type()` and a custom fields plugin like ACF?
`register_post_type()` is a core WordPress function used to define an entirely new content type (e.g., 'Project', 'Service') in your database and admin interface. A custom fields plugin like ACF (Advanced Custom Fields) is then used to add specific data input fields (e.g., 'Client Name', 'Project URL') to these custom post types, enhancing their functionality without having to manually code meta boxes.
Do custom post types affect site performance on a converted WordPress theme?
Properly implemented custom post types generally have minimal impact on performance. The primary factors affecting performance are the number of items queried, the complexity of your queries, and the efficiency of your template code. Using `WP_Query` efficiently and ensuring your theme is well-optimized (as Themify aims to provide with its converted themes) will help maintain good performance.
Can I use custom post types with the Gutenberg editor?
Yes, for your custom post type to be editable with the Gutenberg block editor, ensure that the `show_in_rest` argument is set to `true` when registering your post type using `register_post_type()`. This enables the REST API endpoints necessary for Gutenberg to interact with your custom post type effectively.

Try it in minutes — first conversion free

Themify is the fastest way to turn any live webpage into an installable WordPress theme (.zip). No coding, no rebuilding, no design handoff. Runs 100% locally in your browser.

No credit card required · 14-day money-back guarantee

Chrome browser logoAdd to Chrome — 1 free conversion