I recently developed a WordPress theme for headless WordPress projects. This post is a simple explanation of the decisions that were made after implementing the theme on a few JAMstack projects.
Highlights
The index route forwards to the /wp-admin
login screen so there is no need for any front-end code:
<script type="text/javascript"> window.location.replace(window.location.protocol + "//" + window.location.hostname + "/wp-admin"); </script>
There is an example Custom Post Type already set up to display in the API as well as in the Graph if you are using WPGraphQL:
add_action( "init", "create_custom_post_type" ); function create_custom_post_type() { register_post_type("custom_posts", // Register Custom Post Type array( "labels" => array( "name" => "Custom Posts", // Rename these to suit "singular_name" => "Custom Post", "add_new" => "Add New", "add_new_item" => "Add New Custom Post", "edit" => "Edit", "edit_item" => "Edit Custom Post", "new_item" => "New Custom Post", "view" => "View Custom Post", "view_item" => "View Custom Post", "search_items" => "Search Custom Posts", "not_found" => "No Custom Posts found", "not_found_in_trash" => "No Custom Posts found in Trash" ), "menu_position" => 5, "menu_icon" => "dashicons-awards", "public" => true, "show_in_rest" => true, "show_ui" => true, "show_in_menu" => true, "publicly_queryable" => true, "capability_type" => "page", "hierarchical" => false, "has_archive" => true, "supports" => array("title","thumbnail","editor","revisions","excerpt","author"), // Other Options: trackbacks, custom-fields, page-attributes, comments, post-formats "can_export" => true, // Allows export in Tools > Export "taxonomies" => array(), // Add supported taxonomies, "show_in_graphql" => true, "graphql_single_name" => "CustomPost", "graphql_plural_name" => "CustomPosts", ) ); }
There are example Custom Shortcodes and Custom Taxonomies set up to display in the API as well as in the Graph:
function headless_shortcode( $atts , $content = null ) { // Attributes $args = shortcode_atts( array( "link" => "", "target" => "_self", "rel" => "", "class" => "", ), $atts ); return '<a href="' . $args['link'] . '" target="' . $args['target'] . '" rel="' . $args['rel'] . '" class="btn ' . $args['class'] . '">' . $content . '</a>'; } add_shortcode( "headless", "headless_shortcode" ); add_filter("acf/format_value/type=textarea", "do_shortcode"); function headless_taxonomy() { $labels = array( "name" => "Taxonomies", "singular_name" => "Taxonomy", "menu_name" => "Taxonomies", "all_items" => "All Taxonomies", "parent_item" => "Parent Taxonomy", "parent_item_colon" => "Parent Taxonomy:", "new_item_name" => "New Taxonomy", "add_new_item" => "Add Taxonomy", "edit_item" => "Edit Taxonomy", "update_item" => "Update Taxonomy", "view_item" => "View Taxonomy", "separate_items_with_commas" => "Separate Taxonomies with commas", "add_or_remove_items" => "Add or remove Taxonomies", "choose_from_most_used" => "Choose from the most used", "popular_items" => "Popular Taxonomies", "search_items" => "Search Taxonomies", "not_found" => "Not Found", "no_terms" => "No Taxonomies", "items_list" => "Taxonomies list", "items_list_navigation" => "Taxonomies list navigation", ); $args = array( "labels" => $labels, "hierarchical" => false, "public" => true, "show_ui" => true, "show_in_quick_edit" => false, "meta_box_cb" => false, "show_admin_column" => false, "show_in_nav_menus" => false, "show_tagcloud" => false, "show_in_rest" => true, "show_in_graphql" => true, "graphql_single_name" => "Taxonomy", "graphql_plural_name" => "Taxonomies", ); register_taxonomy( "taxonomy", array( "page" ), $args ); } add_action( "init", "headless_taxonomy", 0 );
There are some utility functions that help with reorganizing/removing menu items and disabling RSS:
function remove_menus() { remove_menu_page( "index.php" ); //Dashboard remove_menu_page( "jetpack" ); //Jetpack* remove_menu_page( "edit-comments.php" ); //Comments } add_action( "admin_menu", "remove_menus" ); function headless_custom_menu_order( $menu_ord ) { if ( !$menu_ord ) return true; return array( "edit.php?post_type=page", // Pages "edit.php", // Posts "edit.php?post_type=custom_posts", // Custom Post Type "separator1", // First separator "upload.php", // Media "themes.php", // Appearance "plugins.php", // Plugins "users.php", // Users "separator2", // Second separator "tools.php", // Tools "options-general.php", // Settings "separator-last", // Last separator ); } add_filter( "custom_menu_order", "headless_custom_menu_order", 10, 1 ); add_filter( "menu_order", "headless_custom_menu_order", 10, 1 );
All ACF fields that are empty are nullified by default:
// Return `null` if an empty value is returned from ACF. if (!function_exists("acf_nullify_empty")) { function acf_nullify_empty($value, $post_id, $field) { if (empty($value)) { return null; } return $value; } } add_filter("acf/format_value", "acf_nullify_empty", 100, 3);
Headless is available over on GitHub. Download it, peruse it, use it and let me know what you think!
Top comments (1)
I was about to create a repository with my own version of a headless theme but I found yours and decided to use it, so far everything is good, great job!