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!