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?


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;

| improve this answer | |
  • 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. – Mojtaba Reyhani Jul 21 '17 at 12:36
  • I have added an example for hook_preprocess_views_view. – oknate Jul 21 '17 at 14:34
  • The best possible answer, I sincerely appreciate your efforts. Thanks for everything ;-) – Mojtaba Reyhani Jul 22 '17 at 23:42
  • why d7's _preprocess_views_view_unformatted hook do not have this ability? any thought.. – kiranking Dec 2 '17 at 15:54

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.

| improve this answer | |
  • 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. – Mojtaba Reyhani Jul 18 '17 at 0:04
  • @MojtabaReyhani I find that very surprising considering the twig says - attributes: The row's HTML attributes. – No Sssweat Jul 18 '17 at 2:33

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

| improve this answer | |
  • 1
    Great to know that twig is natively tracking the loop count. – Kelley Curry Apr 3 '19 at 12:57

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.

| improve this answer | |
  • Thank you so much for your reply, But I want to do this work programmatically and just into the theme layer. – Mojtaba Reyhani Jul 17 '17 at 23:03

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