diff --git a/.gitignore b/.gitignore
index 8b47a5d75bb2bb772116e5d16b85aa03c4e90d19..04bc8b15119d6e0ef53ce3f81cca25ed620aa25d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
 client_secret
 .env
-uhepp_org/uhepp_vault/static/uhepp-js/
+uhepp_org/uhepp_vault/static/react
diff --git a/uhepp-js/src/common.scss b/uhepp-js/src/common.scss
index ec3109d9c86bcd1858dd55caa983d50fe0133286..21bb873363368fe9bb4055d37f802c9aa0741c75 100644
--- a/uhepp-js/src/common.scss
+++ b/uhepp-js/src/common.scss
@@ -8,3 +8,12 @@ $theme-colors: (
 a:hover {
   text-decoration: none;
 }
+
+
+@include media-breakpoint-up(md) { 
+  .uhepp-container {
+    width: 750px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+}
diff --git a/uhepp-js/src/components/Uhepp.jsx b/uhepp-js/src/components/Uhepp.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..87bb0eb831b2a431015e4e8248e45ae7b5190250
--- /dev/null
+++ b/uhepp-js/src/components/Uhepp.jsx
@@ -0,0 +1,34 @@
+import React, { useState } from "react";
+import UheppHistUI from "./UheppHistUI.jsx";
+
+const Error = (message) => (
+  <div className="alert m-3 alert-danger">
+    <h4 className="alert-heading">Rendering failed</h4>
+    <p>The interactive plot rendering failed with the following error message:</p>
+    <p style={{fontFamily: "monospace"}}>{message}</p>
+    <hr />
+    <p>You can download the JSON file and try to fix the issue. Please note that
+    the web rendering is yet not fully supported.</p>
+  </div>)
+
+
+const Uhepp = ({width, height, uhepp}) => {
+  if (!uhepp) {
+    return Error("Invalid uhepp data")
+  }
+  if (!uhepp.version) {
+    return Error("Missing uhepp version")
+  }
+  if (uhepp.version != "0.1") {
+    return Error(`Unsupported uhepp version: ${uhepp.version}`)
+  }
+
+  if (uhepp.type == "histogram") {
+    return  <UheppHistUI width={width} height={height} uhepp={uhepp} />
+  } else {
+    return Error(`Unknown or missing plot type: ${uhepp.type}`)
+  }
+
+}
+
+export default Uhepp;
diff --git a/uhepp-js/src/components/Graph.jsx b/uhepp-js/src/components/UheppHist.jsx
similarity index 95%
rename from uhepp-js/src/components/Graph.jsx
rename to uhepp-js/src/components/UheppHist.jsx
index 3ff7bf3b943a8bae29e707afb881e6f845c1e601..75632671bc5edec19e937a1851804180aeeb461b 100644
--- a/uhepp-js/src/components/Graph.jsx
+++ b/uhepp-js/src/components/UheppHist.jsx
@@ -13,18 +13,20 @@ import { RegisterHTMLHandler } from 'mathjax-full/js/handlers/html';
 import { STATE } from 'mathjax-full/js/core/MathItem';
 import { preprocessData, sumBase, sumStat, histogramify } from "../helpers/uhepp.js";
 
-const getMaxBin =(uhepp) => {
+const getMaxBin = (uhepp) => {
   let max = 0;
-  const n_bins = uhepp.bins.edges.length - 1
+	const edges = uhepp.bins.edges || uhepp.bins.rebin
+  const n_bins = edges.length - 1
   for (let bin_i=0; bin_i < n_bins; bin_i++) {
+    const density_scale = uhepp.bins.density_width ? uhepp.bins.density_width / (edges[bin_i + 1] - edges[bin_i]) : 1
     uhepp.stacks.forEach((stack, stack_index) => {
       let bottom = 0
       stack.content.forEach((stack_item, si_i) => {
-        const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i)
+        const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i) * density_scale
         if (y_value > 0) { bottom += y_value; }
       })
       const whole_stack = stack.content.map(si => si["yield"]).flat()
-      const stat = sumStat(uhepp.yields, whole_stack, bin_i)
+      const stat = sumStat(uhepp.yields, whole_stack, bin_i) * density_scale
       if (stat > 0) { bottom += stat; }
       max = Math.max(max, bottom)
     })
@@ -84,7 +86,11 @@ const Step = ({
 }
 
 
-const computeEquidistent = (edges) => {
+const computeEquidistent = (edges, density) => {
+  if (density) {
+    return density
+  }
+
   let width = false
   for (let i = 0; i < edges.length - 1; i++) {
     const this_width = edges[i + 1] - edges[i]
@@ -261,7 +267,7 @@ const Ratio = ({
       if (isFinite(rel_stat)) {
         objects.splice(0, 0, <rect
           key={`rect-uncert-${ri_i}-${bin_i}`}
-          x={xScale(edges[bin_i - 1])}
+          x={xScale(edges[bin_i])}
           width={xScale(edges[bin_i + 1]) - xScale(edges[bin_i])}
           y={ratioScale(1 + rel_stat)}
           height={ratioScale(1 - rel_stat) - ratioScale(1 + rel_stat)}
@@ -289,13 +295,15 @@ const Histogram = ({
   const n_bins = edges.length - 1
   let objects = []
 
+
   // for (let bin_i=1; bin_i < n_bins; bin_i++) {
     uhepp.stacks.forEach((stack, stack_index) => {
       let bottom = Array(n_bins).fill(0)
       stack.content.forEach((stack_item, si_i) => {
         if (stack.type == "stepfilled") {
           for (let bin_i=0; bin_i < n_bins; bin_i++) {
-            const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i + 1)
+            const density_scale = uhepp.bins.density_width ? uhepp.bins.density_width / (edges[bin_i + 1] - edges[bin_i]) : 1
+            const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i + 1) * density_scale
             objects.push(<Bar leftEdge={edges[bin_i]}
                               rightEdge={edges[bin_i + 1]}
                               bottom={bottom[bin_i]}
@@ -315,7 +323,8 @@ const Histogram = ({
           }
         } else if (stack.type == "step") {
           const bin_indices = Array(n_bins).fill(0).map((_, i) => i)
-          const y_values = bin_indices.map(i => sumBase(uhepp.yields, stack_item["yield"], i + 1))
+          const density_scale = uhepp.bins.density_width ? uhepp.bins.density_width / (edges[bin_i + 1] - edges[bin_i]) : 1
+          const y_values = bin_indices.map(i => sumBase(uhepp.yields, stack_item["yield"], i + 1)) * density_scale
 
           const [new_x, new_y] = histogramify(edges, y_values)
 
@@ -338,8 +347,10 @@ const Histogram = ({
             })
         } else if (stack.type == "points") {
           for (let bin_i=0; bin_i < n_bins; bin_i++) {
-            const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i + 1)
-            const stat = sumStat(uhepp.yields, stack_item["yield"], bin_i + 1)
+            const density_scale = uhepp.bins.density_width ? uhepp.bins.density_width / (edges[bin_i + 1] - edges[bin_i]) : 1
+
+            const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i + 1) * density_scale
+            const stat = sumStat(uhepp.yields, stack_item["yield"], bin_i + 1) * density_scale
             const bin_center = (edges[bin_i + 1] + edges[bin_i]) / 2
             const width = edges[bin_i + 1] - edges[bin_i]
 
@@ -365,8 +376,10 @@ const Histogram = ({
 
 			if (stack.type == "stepfilled" || stack.type == "step") {
         for (let bin_i=0; bin_i < n_bins; bin_i++) {
+          const density_scale = uhepp.bins.density_width ? uhepp.bins.density_width / (edges[bin_i + 1] - edges[bin_i]) : 1
           const whole_stack = stack.content.map(si => si["yield"]).flat()
-          const stat = sumStat(uhepp.yields, whole_stack, bin_i + 1)
+
+          const stat = sumStat(uhepp.yields, whole_stack, bin_i + 1) * density_scale
           objects.push(<rect
             key={`rect-${stack_index}-uncert-${bin_i}`}
             x={xScale(edges[bin_i])}
@@ -593,7 +606,7 @@ const UheppHist = ({width, height, uhepp}) => {
 
 	const xScale = scaleLinear({
 		range: [0, xMax],
-		domain: extent(uhepp.bins.edges),
+		domain: extent(uhepp.bins.rebin || uhepp.bins.edges),
 	})
 	const yScale = scaleLinear({
 		range: [yMax, 0],
@@ -609,10 +622,10 @@ const UheppHist = ({width, height, uhepp}) => {
 
 
 
-  const equidistent = computeEquidistent(uhepp.bins.rebin || uhepp.bins.edges)
+  const equidistent = computeEquidistent(uhepp.bins.rebin || uhepp.bins.edges, uhepp.bins.density_width)
 
 	return (
-  <div style={{width: '100%', margin: '0 auto'}}>
+  <div className="uhepp-container">
 			<svg viewBox={`0 0 ${width} ${height}`}
            onMouseOut={() => setHighlightedBin(null)} >
         <style>
diff --git a/uhepp-js/src/components/UheppHistUI.jsx b/uhepp-js/src/components/UheppHistUI.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..fa50c09f9e1479a69f3e7eaaea57fde795e37d6c
--- /dev/null
+++ b/uhepp-js/src/components/UheppHistUI.jsx
@@ -0,0 +1,118 @@
+import React, { useState } from "react";
+import UheppHist from "./UheppHist.jsx";
+
+const UheppHistUI = ({width, height, uhepp}) => {
+  const [uhepp_data, setData] = useState(uhepp)
+
+  const handleRebin = (e) => {
+    let values = Array.from(e.target.selectedOptions, option => parseFloat(option.value))
+    setData(Object.assign({}, uhepp_data,
+      {bins: Object.assign({}, uhepp_data.bins, {rebin: values})}
+    ))
+  }
+
+  const handleUnderflow = (e) => {
+    let value = e.target.checked
+    setData(Object.assign({}, uhepp_data,
+      {bins: Object.assign({}, uhepp_data.bins, {include_underflow: value})}
+    ))
+  }
+  const handleOverflow = (e) => {
+    let value = e.target.checked
+    setData(Object.assign({}, uhepp_data,
+      {bins: Object.assign({}, uhepp_data.bins, {include_overflow: value})}
+    ))
+  }
+  const handleDensityWidth = (e) => {
+    let value = parseFloat(e.target.value)
+    setData(Object.assign({}, uhepp_data,
+      {bins: Object.assign({}, uhepp_data.bins, {density_width: value})}
+    ))
+  }
+
+  let rebin = uhepp_data.bins.rebin || uhepp_data.bins.edges
+  return <>
+    <UheppHist width={width} height={height} uhepp={uhepp_data} />
+
+		<ul className="nav nav-tabs" id="view-options" role="tablist">
+			<li className="nav-item">
+				<a className="nav-link active" id="info-tab" data-toggle="tab" href="#info" role="tab" aria-controls="info" aria-selected="true">Info</a>
+			</li>
+			<li className="nav-item">
+				<a className="nav-link" id="binning-tab" data-toggle="tab" href="#binning" role="tab" aria-controls="binning" aria-selected="true">Binning</a>
+			</li>
+			<li className="nav-item">
+				<a className="nav-link" id="other-tab" data-toggle="tab" href="#other" role="tab" aria-controls="other" aria-selected="false">Other</a>
+			</li>
+			<li className="nav-item">
+				<a className="nav-link" id="reset-tab" data-toggle="tab" href="#reset" role="tab" aria-controls="reset" aria-selected="false">Reset</a>
+			</li>
+		</ul>
+
+		<div className="tab-content" id="view-options-content">
+			<div className="tab-pane show active" id="info" role="tabpanel" aria-labelledby="binning-tab">
+				<dl>
+					<dt>Author</dt>
+					<dd>{ uhepp.metadata.author }</dd>
+					<dt>Data</dt>
+					<dd>{ uhepp.metadata.data }</dd>
+					<dt>Producer</dt>
+					<dd>{ uhepp.metadata.producer || <i>None</i>}</dd>
+					<dt>Code revision</dt>
+					<dd>{ uhepp.metadata.code_revision || <i>None</i>}</dd>
+				</dl>
+			</div>
+			<div className="tab-pane" id="binning" role="tabpanel" aria-labelledby="binning-tab">
+				<form>
+					<div className="form-group">
+						<label htmlFor="rebin">Bin edges</label>
+						<select multiple size={10} className="form-control" id="rebin" onChange={(e) => handleRebin(e)}>
+							{uhepp.bins.edges.map((v, i) => 
+								<option value={v} key={i} selected={rebin.indexOf(v) >= 0}>{v}</option>
+							)}
+						</select>
+					</div>
+
+					<div className="form-check">
+						<input className="form-check-input" type="checkbox"
+							checked={uhepp_data.bins.include_underflow == true} id="checkUnderflow"
+							onChange={e => handleUnderflow(e)} />
+						<label className="form-check-label" htmlFor="checkUnderflow">
+							Include underflow events in first bin
+						</label>
+					</div>
+
+					<div className="form-check">
+						<input className="form-check-input" type="checkbox"
+							checked={uhepp_data.bins.include_overflow == true} id="checkOverflow" 
+							onChange={e => handleOverflow(e)} />
+						<label className="form-check-label" htmlFor="checkOverflow">
+							Include overflow events in last bin
+						</label>
+					</div>
+
+					<div className="form-group">
+						<label htmlFor="densityWidth">Normalize yields to</label>
+						<div className="input-group">
+							<input type="number" className="form-control" id="densityWidth"
+								onChange={e => handleDensityWidth(e)}
+								value={uhepp_data.bins.density_width || "0"} />
+							<div className="input-group-append">
+								<span className="input-group-text">{uhepp_data.variable.unit}</span>
+							</div>
+						</div>
+					</div>
+				</form>
+			</div>
+			<div className="tab-pane" id="other" role="tabpanel" aria-labelledby="other-tab">
+				<h1>La di da</h1>
+			</div>
+			<div className="tab-pane" id="reset" role="tabpanel" aria-labelledby="reset-tab">
+				<button className="btn btn-secondary m-1" type="button" onClick={() => setData(uhepp)}>
+					Reset view
+				</button>
+			</div>
+		</div>
+  </>
+}
+export default UheppHistUI;
diff --git a/uhepp-js/src/index.js b/uhepp-js/src/index.js
index 2ab49ab9ea3c7b79d44e58edb9c398c83a79bc13..110e05ddc350196a549b319705527e5c1bb94389 100644
--- a/uhepp-js/src/index.js
+++ b/uhepp-js/src/index.js
@@ -4,9 +4,7 @@ import "regenerator-runtime/runtime";
 import './common.scss'
 import 'bootstrap'
 
-import Header from './components/Header.jsx';
-import Graph from './components/Graph.jsx';
-import About from './components/About.jsx';
+import Uhepp from './components/Uhepp.jsx';
 
 import React from "react";
 import ReactDOM from "react-dom";
@@ -16,10 +14,5 @@ import { HashRouter as Router, Route, Link } from "react-router-dom";
 export const fe = {
   React: React, 
   ReactDOM: ReactDOM,
-  Graph: Graph, 
+  Uhepp: Uhepp, 
 };
-
-// ReactDOM.render(
-//   <Graph width="600" height="500" uhepp={toy_data} />,
-//   document.getElementById('app-root')
-// );
diff --git a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html
index 7c26e6746c4cb52aa7e284084a04bae5c9735345..ae87092175f7c8a96517a79fc9ce9e0fb017ac04 100644
--- a/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html
+++ b/uhepp_org/uhepp_vault/templates/uhepp_vault/plot_detail.html
@@ -2,49 +2,104 @@
 {% load pygmentify_tags %}
 
 {% block content %}
+	<a href="{% url 'uhepp_vault:collection-detail' plot.collection.id %}">
+		back to collection
+	</a>
 <h1>{{ plot }}</h1>
 
-<p><a href="{% url 'uhepp_vault:collection-detail' plot.collection.id %}">
-back to collection
-</a></p>
-<p>UUID: {{ plot.uuid }}</p>
-
-<div id="app-root">
-Loading...
-</div>
-{{ plot.uhepp|json_script:"plot-data" }}
 
-<p>
-  <a class="btn btn-primary" href="{% url 'uhepp_vault:plot-download' plot.uuid %}">Download JSON</a>
-</p>
+<div class="d-flex">
+<p>UUID: {{ plot.uuid }}</p>
+<div class="ml-auto btn-group btn-group-sm">
+	<div class="btn-group btn-group-sm">
+		<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
+			Direct download
+		</button>
+		<div class="dropdown-menu dropdown-menu-right p-4 shadow">
+			<p>Download the uhepp data directy as a JSON file.</p>
+			<p>
+				<a class="btn btn-primary" href="{% url 'uhepp_vault:plot-download' plot.uuid %}">Download JSON</a>
+			</p>
+		</div>
+	</div>
 
-<h2>API access</h2>
-<p>Make sure you've set up an API access token.</p>
-<h3>Pull plot</h3>
-{% pygmentify %}
-<pre class="python">
+	<div class="btn-group btn-group-sm">
+		<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
+			API Access
+		</button>
+		<div class="dropdown-menu dropdown-menu-right p-4 shadow">
+			<p class="alert alert-info m-2">
+				Make sure you've set up an <a href="{% url 'uhepp_vault:token-list' %}">API access token</a>.
+			</p>
+			<h3>Pull plot</h3>
+			{% pygmentify %}
+			<pre class="python">
 import uhepp
 plot = uhepp.pull("{{ plot.uuid }}")
 
 # Modify, save, render or show plot locally
 plot.show()</pre>
-{% endpygmentify %}
+	{% endpygmentify %}
 
-<h3>Push new plot to the same collection</h3>
-{% pygmentify %}
-<pre class="python">
+			<h3>Push new plot to the same collection</h3>
+			{% pygmentify %}
+			<pre class="python">
 import uhepp
 # Create a histogram or retrieve one from a location file
 hist = uhepp.from_json("local_file.json")
 hist.push({{ plot.collection.pk }})</pre>
-{% endpygmentify %}
+			{% endpygmentify %}
+		</div>
+	</div>
+
+	<div class="btn-group btn-group-sm">
+		<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
+			CLI tool
+		</button>
+		<div class="dropdown-menu dropdown-menu-right p-4 shadow">
+			<p class="alert alert-info m-2">
+				Make sure you've set up an <a href="{% url 'uhepp_vault:token-list' %}">API access token</a>.
+			</p>
+			<h3>Pull plot</h3>
+			{% pygmentify %}
+			<pre class="console">
+$ uhepp pull {{ plot.uuid}}</pre>
+	{% endpygmentify %}
+
+			<h3>Show plot</h3>
+			{% pygmentify %}
+			<pre class="console">
+$ uhepp show {{ plot.uuid}}</pre>
+	{% endpygmentify %}
+
+			<h3>Push new plot to the same collection</h3>
+			{% pygmentify %}
+			<pre class="console">
+$ uhepp push {{ plot.collection.pk }} local_file.json</pre>
+			{% endpygmentify %}
+		</div>
+  </div>
+</div>
+</div>
+
+<div id="app-root">
+  <div class="d-flex justify-content-center my-5">
+    <div class="spinner-border text-primary" role="status">
+      <span class="sr-only">Loading...</span>
+    </div>
+  </div>
+</div>
+{{ plot.uhepp|json_script:"plot-data" }}
+
+
+
 
 {% endblock %}
 
 {% block loadscript %}
 <script>
 uhepp.fe.ReactDOM.render(
-  uhepp.fe.React.createElement(uhepp.fe.Graph, {
+  uhepp.fe.React.createElement(uhepp.fe.Uhepp, {
     width: "555",
     height: "400",
     uhepp: JSON.parse(document.getElementById('plot-data').textContent)