To do that in our (custom) theme folder in the .theme file we have to add the following hook function: hook_theme_suggestions_HOOK_alter like below:

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function THEME_NAME_theme_suggestions_page_alter(array &$suggestions, array $variables) {

  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $content_type = $node->bundle();
    $suggestions[] = 'page__'.$content_type;
  }
}

Thanks!

Comments

mrksdiehl’s picture

This was exactly the thing I was looking for. Copy paste and it worked. Thank you

Alluuu’s picture

Thank you!

Ravi Shankar Karnati’s picture

Thanks a lot

Jeff Burnz’s picture

I use a different method, however the main difference with my code is that I use array_splice to place the suggestion one above page--node, this preserves the capability to override with page--node--% and page--node--[id]. Your suggestion will always take precedence because you have pushed it onto the array.

If I put them both in you can see this in the suggestions list:

  1. page--node
  2. page--node--page (my method)
  3. page--node--%
  4. page--node--24
  5. page--page (your method)
function THEME_NAME_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  // Add content type suggestions.
  if ($node = \Drupal::request()->attributes->get('node')) {
    array_splice($suggestions, 1, 0, 'page__node__' . $node->getType());
  }
}
kobb’s picture

Perfect! Thank you!

SakaSerbia’s picture

Thank you.. Its really nice..

mohitw’s picture

Worked for me, Thank You

frederickjh’s picture

Thanks Jeff Burnz for this code. I have been using it for some time now. This code works to render suggestion, however it breaks viewing revisions. When trying to view revisions of content I am getting errors like this on the development site: Error: Call to a member function getType() on string in themename_theme_suggestions_page_alter() (line 47 of themes/custom/themename/themename.theme).

Here are lines 40-49 from themename.theme:

/**
 * Implements hook_theme_suggestions_page_alter().
 * https://www.drupal.org/node/2521876#comment-10684366
 */
function themename_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  // Add content type suggestions.
  if ($node = \Drupal::request()->attributes->get('node')) {
    array_splice($suggestions, 1, 0, 'page__node__' . $node->getType());
  }
}

I have modified the code to this using a suggestion from a comment on code to check that revisions wasn't present in the URL. With this code it is possible to view revision and template suggestions are presented only on the current version of the content.

/**
 * Implements hook_theme_suggestions_page_alter().
 * https://www.drupal.org/node/2521876#comment-10684366
 */
function themename_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  // Add content type suggestions.
  if (($node = \Drupal::request()->attributes->get('node')) && (strpos($_SERVER['REQUEST_URI'], "revisions") == false)) {
    array_splice($suggestions, 1, 0, 'page__node__' . $node->getType());
  }
}

Frederick

rgalaxy’s picture

very very very very very thank you for this code, its working like a charm !
i've tried to figure out how to make it works and with this it works just fine :D

Melvin Loos’s picture

I wouldn't recommend using the php strpos function with only two equal signs (==) in this case. As the PHP documentation mentions, "Note our use of ===. Simply == would not work as expected because the position of 'a' was the 0th (first) character.". A strict checking of values is required when checking the strpos return value as this function returns FALSE when not found but the actual position when it IS found. See the manual here.

Dev Binod’s picture

Worked for me too.
You saved me

Thank you so much.

serverjohn’s picture

4 years later and this still works. Viewing revisions caused my site to crash but with this little bit of code everything works like a dream. THANK YOU.

VarunKrSingh’s picture

Hi, The Page view is coming but Page title is coming as Page not found and it is showing page view source as
Page not found | WEBSITE_NAME
The page content is coming fine and problem is only with title.

Please assist.

Amgad Hassan’s picture

Thank you very much ,,it's worked with me .

rafaelfmedeiros’s picture

It works perfectly! Thank you!

dark_diesel’s picture

Thank you!

luvsharma’s picture

Thank you!

abdel-el’s picture

Hi everyone, I'm new with drupal 8 and i would like to know how to overridinde node--(type).html.twig.

whene im using the Available variables like:

{{ label }} or {{ content.field_example }} i have an additional html conent that i dont need, i juste want the value.

someone could help me please.

thanks!

walsamlees’s picture

Though I'm new to drupal, I couldn't get this to work. Is there anything I'm doing wrong?

rgalaxy’s picture

yeah me also.. been doing this for 3 months to the point i felt im really just a stupid :<
hope you find answer and able to help me... this is my problem

walsamlees’s picture

save as "theme_name.theme" that should do it

walsamlees’s picture

Though I spent time trying to figure it out. My major mistake was I was saving it as theme_name.theme.php (initially as theme_name.theme.inc). Thanks all d same

nikosprodro’s picture

+1 from me. It works like a charm.

vanphat_92’s picture

when i'm using this method, somehow the page doesn't load javascript from core

chasep01’s picture

This is my first time messing with templates in general, and based on the comments above it's probably me doing something wrong.

First I created a content type. "adventures" the I created a template for it. "page--adventures.html.twig". After creating both I added the following to my .theme file:

/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function bootstrap_subtheme_theme_suggestions_page_alter(array &$suggestions, array $variables) {

if ($node = \Drupal::routeMatch()->getParameter('adventures')) {
$content_type = $node->bundle();
$suggestions[] = 'page__adventures'.$content_type;
}
}

Nothing seems to change. The content type still used the original template. Any suggestions? Thanks in advance! It's been driving me insane.

walsamlees’s picture

Delete "adventures" from $suggestions[] = 'page__adventures'.$content_type;

ARUN AK’s picture

Try to change your code in to

if ($node = \Drupal::routeMatch()->getParameter('node')) {
....
}

Thanks and Regards
ARUN AK
www.developersdocs.com

biggm’s picture

I noticed that in 8.3 the node id and revision id are both strings if you're viewing a revision. In order to get the full entity so I could get the bundle value I needed. This was how the code evolved. I'm sure there are better ways but this got me there.

  $node     = \Drupal::routeMatch()->getParameter('node');
  $revision = \Drupal::routeMatch()->getRawParameter('node_revision');


  //revision IDs are strings and node ids are strings when a revision is loaded.
  //node parameters loaded by getParameter are objects and the bundle can be accessed normally.
  if ($node) {
    if ($revision) {
      $revised_node = \Drupal::entityTypeManager()
                             ->getStorage('node')
                             ->loadRevision($revision);

      $content_type = $revised_node->bundle();
    }
    else {
      $content_type = $node->bundle();
    }

    $suggestions[] = 'page__' . $content_type;
  }
mocasalter’s picture

That worked for me, thanks!

shailja179’s picture

Hi, i am new to drupal8.
I am wondering that how to add node edit form template.

ganesh9930’s picture

function NAMEOFTHEME_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if ($hook == 'node_edit_form') {
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      $content_type = $node->bundle();
    } else {
      $current_path = \Drupal::service('path.current')->getPath();
      $path_args = explode('/', $current_path);
      $content_type = $path_args[3];
    }
    $suggestions[] = 'node_edit_form__' . $content_type;
  }
}

create twig templates in your theme's template directory in the form of node-edit-form--NODE-TYPE-SEPARATED-WITH-DASHES.html.twig.

phu.nguyen’s picture

It works great.

kennybell’s picture

Thanks!

Thomashdk’s picture

In what file should I add the code? and should the functions be called somewhere?

I have an
THEME_NAME.theme.yml
THEME_NAME.module

??

// Thomas

kennybell’s picture

@Thomashdk add the code in your: THEME_NAME.theme and refresh the cache. The function should be call automatically.

Thomashdk’s picture

When I have the code in my THEME_NAME.theme (cabana.theme) I only get this
https://codeshare.io/thomashdk ( it's the markup)

kennybell’s picture

ok, which is the name of the Content type, and the page--(content type name).html.twig that you are using? @Thomashdk

hellobank’s picture

And I want to add below twig templates based on content type, How to ?

1. html.twig
2. html--article.html.twig
3. html--blog.html.twig
...

blairski’s picture

If you use the same function but change *page_alter* to *html_alter*, it should work. i.e.:

/**
 * Implements hook_theme_suggestions_html_alter().
 */
function example_theme_suggestions_html_alter(array &$suggestions, array $variables) {
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
	$suggestions[] = 'html__' . $node->bundle();
  }
}
vinut37’s picture

Dear all, I am newer to drupal and doing my first website.
I enabled twig debug but its not showing correct page suggestions for basic pages.
It is showing page suggestions with page title for my event page.(I used views to create this page)

But its showing page suggestions without title for services page (its a basic page)

Any one can help me?

amy.h’s picture

I can't seem to get it to work.  I'm using

function mytheme_theme_suggestions_block_alter(array &$suggestions, array $variables) {
        $suggestions[] = 'wide_grid_block';
}

and wide_grid_block.html.twig exists in the templates folder and even appears in the list of suggested files in debug but it's not used. 

debasisnaskar’s picture

Hi, I have used this above code for Implements hook_theme_suggestions_HOOK_alter(). After that I was trying to add the field value through twig like {content.field_product_price} on the page--mypage.html.twig, its not giving the output. Please advise me how to get the custom field value. Even I have added another field(type is image/file) i.e. field_prod_image, then How will I get the image link ???

Thanks inadvance :-)

blairski’s picture

You can access content type fields in page templates using the node object. So for field_product_price, it should be:

{{ node.field_product_price }}
debasisnaskar’s picture

Its not Working. Infact I have used {node.field_product_price.value}, but its printing the raw output including html tag.

drupaal’s picture

Hi, similar approach but I need it for a custom form that is build in a custom module. There's no content-type created just a multi-step form in a custom module. I need to make a custom twig template for the form and the fields. How can I do that?

My custom module name is: nemo and custom theme name is: nemo_theme

Thanks.

joaojunior.mail’s picture

function hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables)
{
    $node = \Drupal::request()->get('node_preview') ?: \Drupal::request()->get('node');
    if (!empty($node)) {
        $suggestions[] = 'page__' . $node->getType();
    }
}
karolus’s picture

I came across this issue recently, and upon doing some digging, found another solution, thanks to BeFused at this link:

/**
 * Implements hook_theme_suggestions_page_alter().
 */
function example_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $suggestions[] = 'page__' . $node->bundle();
  }
}
frederico’s picture

Thanks karolus!!  This works for me. I also appreciate the link to BeFused and their explanation on how this works.  

ydahi’s picture

Is there any way to create a theme suggestion for page.html.twig for custom view modes?

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function THEME_NAME_theme_suggestions_page_alter(array &$suggestions, array $variables) {

  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $content_type = $node->bundle();
    $view_mode = $node->view_mode()  ##<-- how do you get view_mode here??
    $suggestions[] = 'page__' . $content_type . '__' . $view_mode;
  }
}
sacarney’s picture

I have exactly the same question.

ydahi’s picture

I was able to do this using the Context module.

I created a new context with:

  1. Condition: Content type = My Content Type (machine name: mycontenttype)
  2. Reaction: Page Template Suggestion = page__mycontenttype__viewmode
  3. Created a template file from page.html.twig called page--mycontenttype--viewmode.html.twig

Hope that helps others.

JFeltkamp’s picture

When you render a page it is always the "full" view mode. Or do I miss your point? 

levmyshkin’s picture

Display Suite module allows to select View mode for node page:
https://www.drupal.org/project/ds

sandeep_singh’s picture

function sphynx_theme_suggestions_node_alter(array &$suggestions, array $variables) {

$suggestions[] = 'node__helloo';

}

twig template name will be node--helloo.html.twig