diff --git a/src/metabase/pulse/render/body.clj b/src/metabase/pulse/render/body.clj
index aa9106c158677441c0320754b52d62a826546565..7c6f7f544ec04f197b09dad2d714aa4695619a3b 100644
--- a/src/metabase/pulse/render/body.clj
+++ b/src/metabase/pulse/render/body.clj
@@ -215,13 +215,17 @@
        (list table-body))}))
 
 (s/defmethod render :bar :- common/RenderedPulseCard
-  [_ render-type _timezone-id :- (s/maybe s/Str) card {:keys [cols] :as data}]
+  [_ render-type _timezone-id :- (s/maybe s/Str) card {:keys [cols rows] :as data}]
   (let [[x-axis-rowfn y-axis-rowfn] (common/graphing-column-row-fns card data)
-        rows                        (common/non-nil-rows x-axis-rowfn y-axis-rowfn (:rows data))
+        rows                        (map (juxt x-axis-rowfn y-axis-rowfn)
+                                         (common/non-nil-rows x-axis-rowfn y-axis-rowfn rows))
+        svg-fn                   (if (isa? (-> cols x-axis-rowfn :effective_type) :type/Temporal)
+                                   js-svg/timelineseries-bar
+                                   js-svg/categorical-bar)
         image-bundle                (image-bundle/make-image-bundle
                                       render-type
-                                      (js-svg/timelineseries-bar
-                                        (mapv (juxt x-axis-rowfn y-axis-rowfn) rows)
+                                      (svg-fn
+                                        rows
                                         {:bottom (-> cols x-axis-rowfn :display_name)
                                          :left   (-> cols y-axis-rowfn :display_name)}))]
     {:attachments
diff --git a/src/metabase/pulse/render/js_svg.clj b/src/metabase/pulse/render/js_svg.clj
index 052a5e4f1f15ecdf99c219f14a8a431c9fb64555..45b4203a726479b78ca32f52cf47d1b85f02b8d4 100644
--- a/src/metabase/pulse/render/js_svg.clj
+++ b/src/metabase/pulse/render/js_svg.clj
@@ -45,6 +45,11 @@ const date_accessors = {
   y: (row) => row[1],
 }
 
+const positional_accessors = {
+  x: (row) => row[0],
+  y: (row) => row[1],
+}
+
 const dimension_accessors = {
   dimension: (row) => row[0],
   metric: (row) => row[1],
@@ -66,6 +71,14 @@ function timeseries_bar (data, labels) {
  })
 }
 
+function categorical_bar (data, labels) {
+  return StaticViz.RenderChart(\"categorical/bar\", {
+    data: toJSArray(data),
+    labels: toJSMap(labels),
+    accessors: positional_accessors
+ })
+}
+
 function categorical_donut (rows, colors) {
   return StaticViz.RenderChart(\"categorical/donut\", {
     data: toJSArray(rows),
@@ -157,12 +170,20 @@ function categorical_donut (rows, colors) {
 
 (defn timelineseries-bar
   "Clojure entrypoint to render a timeseries bar char. Rows should be tuples of [datetime numeric-value]. Labels is a
-  map of {:left \"left-label\" :right \"right-label\"}. Returns a byte array of a png file"
+  map of {:left \"left-label\" :right \"right-label\"}. Returns a byte array of a png file."
   [rows labels]
   (let [svg-string (.asString (js/execute-fn-name @context "timeseries_bar" rows
                                                   (map (fn [[k v]] [(name k) v]) labels)))]
     (svg-string->bytes svg-string)))
 
+(defn categorical-bar
+  "Clojure entrypoint to render a categorical bar chart. Rows should be tuples of [stringable numeric-value]. Labels is
+  a map of {:left \"left-label\" :right \"right-label\". Returns a byte array of a png file. "
+  [rows labels]
+  (let [svg-string (.asString (js/execute-fn-name @context "categorical_bar" rows
+                                                  (map (fn [[k v]] [(name k) v]) labels)))]
+    (svg-string->bytes svg-string)))
+
 (defn categorical-donut
   "Clojure entrypoint to render a categorical donut chart. Rows should be tuples of [category numeric-value]. Returns a
   byte array of a png file"