Is it possible by using the theme preprocessor add some classes to views generated rows?

For example: based on Drupal site about default theme implementation to display a view of unformatted rows.

Available variables:
- title: The title of this group of rows. May be empty.
- rows: A list of the view's row items.
- attributes: The row's HTML attributes.
- content: The row's content.
- view: The view object.
- default_row_class: A flag indicating whether default classes should be used on rows.

I know that it's possible and we can add our desired classes to each row inside the views-view-unformatted.html.twig:

{% if title %}
  <h3>{{ title }}</h3>
{% endif %}
{% for row in rows %}
    set row_classes = [
      default_row_class ? 'views-row',
  <div{{ row.attributes.addClass(row_classes) }}>
    {{ row.content }}
{% endfor %}

But I want to understand is it possible to do the same thing for custom views like front-page by use of:
function template_preprocess_views_view_unformatted__frontpage in particular
or in other template_preprocess_views_view generally?

4 Answers 4


If you look at /core/modules/views/views.theme.inc at template_preprocess_views_view_unformatted you'll see this code:

function template_preprocess_views_view_unformatted(&$variables) {
  $view = $variables['view'];
  $rows = $variables['rows'];
  $style = $view->style_plugin;
  $options = $style->options;

  $variables['default_row_class'] = !empty($options['default_row_class']);
  foreach ($rows as $id => $row) {
    $variables['rows'][$id] = [];
    $variables['rows'][$id]['content'] = $row;
    $variables['rows'][$id]['attributes'] = new Attribute();
    if ($row_class = $view->style_plugin->getRowClass($id)) {

This is how if you have set a row class in the view, it gets added.

enter image description here

And the resulting output:

enter image description here

You can copy this function to your custom module and customize it as you like. For example, this will add the "views-row-" plus the name of your view cleaned up using the pathauto cleanstring utility:

function mymodule_preprocess_views_view_unformatted(&$variables) {
  $view = $variables['view'];
  $rows = $variables['rows'];

  foreach ($rows as $id => $row) {
    // Add custom class to each row that encorporates the view id.
    $custom_row_class = \Drupal::service('pathauto.alias_cleaner')
    $custom_row_class = 'views-row-' . $custom_row_class;

If you would like to add classes to a views rows from hook_preprocess_views_view, you can do it this way:

function mitsloan_global_preprocess_views_view(&$variables) {
  $view = $variables['view'];

  if ($view->id() == 'testview') {

    $row_style_plugin = &$variables['rows'][0]['#view']->style_plugin;

    $custom_row_class = 'views-row-' . \Drupal::service('pathauto.alias_cleaner')

    $row_style_plugin->options['row_class'] = $custom_row_class;

  • Thank you so much, I appreciate you greatly, This code works correctly for hook_preprocess_views_view_unformatted but it seems that not works in hook_preprocess_views_view. Jul 21, 2017 at 12:36
  • I have added an example for hook_preprocess_views_view.
    – oknate
    Jul 21, 2017 at 14:34
  • The best possible answer, I sincerely appreciate your efforts. Thanks for everything ;-) Jul 22, 2017 at 23:42
  • why d7's _preprocess_views_view_unformatted hook do not have this ability? any thought..
    – kiranking
    Dec 2, 2017 at 15:54
  • An alternative to using pathauto is Drupal\Component\Utility\Html::cleanCssIdentifier(), although it doesn't give you as much control over the output as Pathauto.
    – imclean
    Mar 10, 2023 at 0:29

You can also check various loop variables defined by twig and use them to apply unique classes, e.g.

{% for row in rows %}
    {% if (loop.index == 1 ) %}
        {% set is_active = 'is-active' %}
    {% else %}
        {% set is_active = '' %}
    {% endif %}
    <li class="{{ is_active }} counter{{ loop.index }}">
        {{ row.content }}
{% endfor %}

loop.index The current iteration of the loop. (1 indexed)

loop.index0 The current iteration of the loop. (0 indexed)

loop.revindex The number of iterations from the end of the loop (1 indexed)

loop.revindex0 The number of iterations from the end of the loop (0 indexed)

loop.first True if first iteration

loop.last True if last iteration

loop.length The number of items in the sequence

loop.parent The parent context

  • 1
    Great to know that twig is natively tracking the loop count. Apr 3, 2019 at 12:57
  • 1
    Minimal code solution!
    – VVS
    Jan 25, 2021 at 13:40

Not tested, but I think this will work:

function template_preprocess_views_view(&$variables) {
  $view = $variables['view'];
  $id = $view->storage->id();

  if ($id == 'view_name' && $view->current_display == 'block_1') {
    $variables['attributes']['class'][] = 'custom-class-name';

Note the $view->current_display is the machine name.

  • You can remove the && $view->current_display == 'block_1' part if you don't want it to be this specific.

  • You can remove the if statement if you want to affect ALL views.

  • Thank you so much, I appreciate you greatly, This code works correctly for Drupal 8, but adds classes to the parent item, not to the each child rows. Jul 18, 2017 at 0:04
  • @MojtabaReyhani I find that very surprising considering the twig says - attributes: The row's HTML attributes.
    – No Sssweat
    Jul 18, 2017 at 2:33

Does it need to be conditional or just a view added to each row? You can do that in the UI. The front page rows can have a class added to them by doing to /admin/structure/views/view/frontpage and clicking on "settings" next to "unformatted list". This has a row class field which might be what you're after.

  • Thank you so much for your reply, But I want to do this work programmatically and just into the theme layer. Jul 17, 2017 at 23:03

Not the answer you're looking for? Browse other questions tagged or ask your own question.