diff --git a/a/modules/leaflet_views/src/Plugin/views/style/LeafletMap.php b/b/modules/leaflet_views/src/Plugin/views/style/LeafletMap.php
index 8409969..522bb36 100644
--- a/a/modules/leaflet_views/src/Plugin/views/style/LeafletMap.php
+++ b/b/modules/leaflet_views/src/Plugin/views/style/LeafletMap.php
@@ -888,11 +888,15 @@
 
     if (!empty($geofield_names) && (!empty($this->view->result) || !$this->options['hide_empty_map'])) {
       // Group the rows according to the grouping instructions, if specified.
-      $view_results_groups = $this->renderGrouping(
-        $this->view->result,
-        $this->options['grouping'],
-        TRUE
-      );
+      // Avoid StylePluginBase::renderGrouping()'s unconditional renderFields()
+      // call when nothing is actually grouped: it renders every configured
+      // field for every row through the full Render API, a large and
+      // needless cost with hundreds/thousands of map features. Fields are
+      // still rendered lazily, on demand, by whichever option actually
+      // needs them (see ensureRenderedFields()).
+      $view_results_groups = !empty($this->options['grouping'])
+        ? $this->renderGrouping($this->view->result, $this->options['grouping'], TRUE)
+        : ['' => ['group' => '', 'rows' => $this->view->result]];
       asort($view_results_groups);
 
       // Process results and create features.
@@ -1204,6 +1208,24 @@
   }
 
   /**
+   * Ensures fields have been rendered, lazily, only when actually needed.
+   *
+   * StylePluginBase::renderFields() renders every configured field for
+   * every row through the full Render API - expensive with hundreds/
+   * thousands of map features. Most per-feature options (weight, icon,
+   * className, ajax popups) only need raw field values and use
+   * getFieldValue() instead (cheap, no Render API). This is called only by
+   * the few remaining consumers (legacy popup/tooltip/marker-cluster-
+   * exclude options) that genuinely need the rendered/formatted output, so
+   * views that don't use those options never pay the renderFields() cost.
+   */
+  protected function ensureRenderedFields(): void {
+    if (!isset($this->rendered_fields)) {
+      $this->renderFields($this->view->result);
+    }
+  }
+
+  /**
    * Applies Views token replacement only when the text actually has tokens.
    *
    * PluginBase::viewsTokenReplace() always renders the text through the
@@ -1303,6 +1325,9 @@
 
       default:
         // Row rendering of single specified field value (without labels).
+        if (!empty($popup_source)) {
+          $this->ensureRenderedFields();
+        }
         $popup_content = !empty($popup_source) ? $this->rendered_fields[$result->index][$popup_source] : '';
     }
 
@@ -1342,8 +1367,9 @@
    */
   protected function prepareTokens(mixed $result): array {
     $tokens = [];
-    foreach ($this->rendered_fields[$result->index] as $field_name => $field_value) {
-      $tokens[$field_name] = $field_value;
+    foreach (array_keys($this->view->field) as $field_name) {
+      $values = $this->getFieldValue($result->index, $field_name);
+      $tokens[$field_name] = is_array($values) ? reset($values) : $values;
     }
     return $tokens;
   }
@@ -1434,6 +1460,7 @@
     if ($this->options['leaflet_markercluster'] &&
         $this->options['leaflet_markercluster']['control'] &&
         !empty($this->options['leaflet_markercluster']['excluded'])) {
+      $this->ensureRenderedFields();
       $excluded_from_markercluster_option = $this->rendered_fields[$result->index][$this->options['leaflet_markercluster']['excluded']] ?? NULL;
       $feature['markercluster_excluded'] = !empty(str_replace(
         ["\n", "\r"],
@@ -1482,6 +1509,7 @@
         default:
           // Decode every entity because JS will encode them again,
           // and we don't want double encoding.
+          $this->ensureRenderedFields();
           $feature['tooltip']['value'] = array_key_exists($this->options['leaflet_tooltip']['value'], $this->rendered_fields[$result->index]) ?
             Html::decodeEntities((string) $this->rendered_fields[$result->index][$this->options['leaflet_tooltip']['value']]) : '';
       }
@@ -1499,8 +1527,10 @@
     elseif ($this->options['name_field']) {
       // Decode every entity because JS will encode them again,
       // and we don't want double encoding.
-      $feature['title'] = !empty($this->options['name_field']) ?
-        Html::decodeEntities((string) $this->rendered_fields[$result->index][$this->options['name_field']]) : '';
+      $name_field_values = !empty($this->options['name_field']) ?
+        $this->getFieldValue($result->index, $this->options['name_field']) : NULL;
+      $feature['title'] = !empty($name_field_values) ?
+        Html::decodeEntities((string) reset($name_field_values)) : '';
     }
   }
 
