diff --git a/beat/web/reports/api.py b/beat/web/reports/api.py
index 2a38acc8f751e431faf1c0a469f162a7f2dac295..ae7fe4af08875a5c4fa520675eb3163c057f7431 100644
--- a/beat/web/reports/api.py
+++ b/beat/web/reports/api.py
@@ -68,6 +68,8 @@ from ..common.responses import BadRequestResponse, ForbiddenResponse
 
 import re
 
+from django.utils.encoding import force_bytes, force_text
+
 import simplejson as json
 
 
@@ -546,3 +548,27 @@ class ReportResultsAllExperimentsView(CommonContextMixin, generics.RetrieveAPIVi
                 results[experiment.fullname()] = serializer.data
 
         return Response(results)
+
+
+#----------------------------------------------------------
+
+
+class ReportRSTCompileView(BaseReportActionView):
+    permission_classes = BaseReportActionView.permission_classes + [IsEditable]
+
+    def post(self, request, owner_name, report_name):
+        result = {}
+
+        try:
+            from docutils.core import publish_parts
+        except ImportError:
+            if settings.DEBUG:
+                raise template.TemplateSyntaxError("Error in ReportRSTCompileView: The Python docutils library isn't installed.")
+            result['html_str'] = force_text(value)
+        else:
+            docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
+            parts = publish_parts(source=force_bytes(request.data['raw']), writer_name="html4css1", settings_overrides=docutils_settings)
+            result['html_str'] = force_text(parts["fragment"])
+
+        return Response(result)
+        #return BadRequestResponse(result)
diff --git a/beat/web/reports/api_urls.py b/beat/web/reports/api_urls.py
index f8d4017819df11bc44cd09b3717f8fe273008e06..9a0038c25bb3e039fb98f0055a0c28bf6ba4d38b 100644
--- a/beat/web/reports/api_urls.py
+++ b/beat/web/reports/api_urls.py
@@ -30,6 +30,12 @@ from . import api
 
 
 urlpatterns = [
+    url(
+        r'^(?P<owner_name>\w+)/(?P<report_name>[\w\W]+)/rst/$',
+        api.ReportRSTCompileView.as_view(),
+        name='rst_compiler'
+    ),
+
     url(
         r'^$',
         api.ReportListView.as_view(),
diff --git a/beat/web/reports/static/reports/app/app.js b/beat/web/reports/static/reports/app/app.js
index c765092820e139e2ec61c0c6f098ffc8f7551e6a..663256161a1b9cfeaeda43da27c3ffb0befa8836 100644
--- a/beat/web/reports/static/reports/app/app.js
+++ b/beat/web/reports/static/reports/app/app.js
@@ -19,7 +19,7 @@
  * You should have received a copy of the GNU Affero Public License along
  * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
  */
-angular.module('reportApp', ['ui.router', 'angular.filter', 'ui.sortable']);
+angular.module('reportApp', ['ui.router', 'angular.filter', 'ui.sortable', 'ui.codemirror']);
 
 
 angular.module('reportApp').config(function ($stateProvider, $urlRouterProvider){
diff --git a/beat/web/reports/static/reports/app/controllers/reportController.js b/beat/web/reports/static/reports/app/controllers/reportController.js
index eecfe0b68445db57b8c9583199119c2e852edcd0..a27174dd3a5dfafed522b5fd7cd99515ae3a01dc 100644
--- a/beat/web/reports/static/reports/app/controllers/reportController.js
+++ b/beat/web/reports/static/reports/app/controllers/reportController.js
@@ -101,6 +101,7 @@ angular.module('reportApp').controller('reportController',['$scope', 'reportFact
 
 		// save groups data to the model
 		GroupsService.loadGroups($scope.report.content.groups);
+		GroupsService.saveUrlBase(`/${$scope.user}/${$scope.report_id}`);
 	}
 
 	function getReportData(user, report_id){
diff --git a/beat/web/reports/static/reports/app/directives/groupTableItem.js b/beat/web/reports/static/reports/app/directives/groupTableItem.js
index 16215ecf4f17340686e0edd4a601f7747f238a14..b319e4ac36307b9fd0df9fafc43d8ecaa1c04f16 100644
--- a/beat/web/reports/static/reports/app/directives/groupTableItem.js
+++ b/beat/web/reports/static/reports/app/directives/groupTableItem.js
@@ -134,12 +134,16 @@ angular.module('reportApp')
 				;
 
 				let str = '';
-				let fieldsStr = fields.join(',');
+
+				let fieldsStr = fields
+				.map(f => `${f}(${scope.getFieldType(f)})`)
+				.join(',');
+
 				let expsStrs = exps
-				.map(e => fields.map(f => `${scope.getFieldVal(e, f)}(${scope.getFieldType(f)})`).join(','))
+				.map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(','))
 				.join('\n');
 
-				str = `${fieldsStr}\n${expsStrs}`;
+				str = `<pre>${fieldsStr}\n${expsStrs}</pre>`;
 
 				return str;
 			};
diff --git a/beat/web/reports/static/reports/app/directives/groupTextItem.js b/beat/web/reports/static/reports/app/directives/groupTextItem.js
index 6bb7ea5eb7b0f916604eff4d7a6073c60d60eb8e..68caadfcb80464f90cfe3d3d3f0a1c8e1f8e7dec 100644
--- a/beat/web/reports/static/reports/app/directives/groupTextItem.js
+++ b/beat/web/reports/static/reports/app/directives/groupTextItem.js
@@ -26,7 +26,7 @@
  * 	displays a plot report item
  */
 angular.module('reportApp')
-.directive("groupTextItem", [function(){
+.directive("groupTextItem", ['GroupsService', function(GroupsService){
 	return {
 		scope: {
 			group: '=',
@@ -37,45 +37,22 @@ angular.module('reportApp')
 		link: function(scope){
 			scope.item = scope.reportItem;
 			scope.domId = `${scope.group.name}_${scope.item.id}`;
-			const editorOptions = {
-				modules: {
-					toolbar: [
-						[{ header: [1, 2, false] }],
-						['bold', 'italic', 'underline'],
-						['image', 'code-block']
-					]
-				},
-				theme: 'snow'
-			};
-
-			// the text editor
-			scope.editor;
-			scope.txt = { val: '' };
 
-			let setupEditor = (el) => {
-				scope.editor = new Quill(el, editorOptions);
-				if(typeof scope.item.content === 'object'){
-					scope.editor.setContents(scope.item.content);
-					scope.txt.val = scope.editor.getText();
-				}
+			scope.uicmOptions = {
+				mode: 'rst',
 			};
 
-			let editorId = `${scope.domId}-text-editor`;
-			scope.$watch(() => document.querySelector(`#${editorId}`),
-				(oldEl, newEl) => {
-					if(newEl){
-						setupEditor(newEl);
-					}
-				}
-			);
 
-			scope.saveContent = () => {
-				let newContent = scope.editor.getContents();
-				scope.item.content = newContent;
-				scope.txt.val = scope.editor.getText();
+			scope.compiledContent = { val: '' };
+			scope.serializeFuncObj.val = () => {
+				return scope.compiledContent.val;
+			};
+			scope.compileContent = () => {
+				GroupsService.compileRST(scope.item.content)
+				.then(data => {
+					scope.compiledContent.val = data.data.html_str;
+				});
 			};
-
-			scope.serializeFuncObj.val = () => scope.txt;
 		},
 		template: `
 <div id="{{domId}}-heading" class="panel-heading" role="tab">
@@ -91,8 +68,8 @@ angular.module('reportApp')
 			{{ domId }}
 		</a>
 		<div class="btn-group" role="group" role='tab'>
-			<button class='btn btn-success' ng-click='saveContent()'>
-				Save Content
+			<button class='btn btn-success' ng-click='compileContent()'>
+				Compile Content
 			</button>
 			<button class='btn btn-default' ng-click='showSerialized.val = !showSerialized.val'>
 				Toggle Serialize View
@@ -108,7 +85,12 @@ angular.module('reportApp')
 	class="panel-collapse collapse in"
 	role="tabpanel"
 	aria-labelledby="{{domId}}-heading">
-	<div id='{{domId}}-text-editor'></div>
+	<div class='panel-body'>
+		<ui-codemirror ng-model='item.content' ui-codemirror-opts='uicmOptions'></ui-codemirror>
+		<p class='help-block'>
+		Describe the object thoroughly using <a href="http://docutils.sourceforge.net/rst.html">reStructuredText mark-up</a><br><i class="fa fa-thumbs-up"></i> The ruler at 80 columns indicate suggested <a href="https://en.wikipedia.org/wiki/POSIX">POSIX line breaks</a> (for readability).<br><i class="fa fa-thumbs-up"></i> The editor will automatically enlarge to accomodate the entirety of your input<br><i class="fa fa-thumbs-up"></i> Use <a href="http://codemirror.net/doc/manual.html#commands">keyboard shortcuts</a> for search/replace and faster editing. For example, use Ctrl-F (PC) or Cmd-F (Mac) to search through this box
+		</p>
+	</div>
 </div>
 `
 	};
diff --git a/beat/web/reports/static/reports/app/directives/groupViewSerialized.js b/beat/web/reports/static/reports/app/directives/groupViewSerialized.js
index 558a0c2448602c98471c7cd8a7063192ffec2861..540a17c964f235e13f0bfeec057b92f95dea844e 100644
--- a/beat/web/reports/static/reports/app/directives/groupViewSerialized.js
+++ b/beat/web/reports/static/reports/app/directives/groupViewSerialized.js
@@ -26,19 +26,18 @@
  * 	displays a plot report item
  */
 angular.module('reportApp')
-.directive("groupViewSerialized", [function(){
+.directive("groupViewSerialized", ['$sce', function($sce){
 	return {
 		scope: {
 			entity: '=',
 			serializeFuncObj: '='
 		},
 		link: function(scope, el){
+			scope.trustAsHtml = $sce.trustAsHtml;
 		},
 		template: `
 <div class='well'>
-<pre>
-{{ serializeFuncObj.val() }}
-</pre>
+	<div ng-bind-html='trustAsHtml(serializeFuncObj.val())'></div>
 </div>
 `
 	};
diff --git a/beat/web/reports/static/reports/app/factories/reportFactory.js b/beat/web/reports/static/reports/app/factories/reportFactory.js
index d7bb01802574db43bd802553fa16b6bbd36c7568..4e930ae3d2a0ee479b1d546bd6f78e1c82b77148 100644
--- a/beat/web/reports/static/reports/app/factories/reportFactory.js
+++ b/beat/web/reports/static/reports/app/factories/reportFactory.js
@@ -94,5 +94,20 @@ angular.module('reportApp').factory('reportFactory', ['$http', 'experimentFactor
 		});
 	};
 
+	reportFactory.compileRST = (urlSubstr, raw) => {
+		let data = {
+			raw
+		};
+
+		let url = `${urlBase}${urlSubstr}`;
+
+		return $http({
+			headers: {'Content-Type': 'application/json'},
+			url,
+			method: "POST",
+			data
+		});
+	};
+
 	return reportFactory;
 }]);
diff --git a/beat/web/reports/static/reports/app/services/groupsService.js b/beat/web/reports/static/reports/app/services/groupsService.js
index 887a0482d0d7897946a6b3b408b0a94a964d5530..fc86d107df4daa3014984277bfb8e577b9135a5e 100644
--- a/beat/web/reports/static/reports/app/services/groupsService.js
+++ b/beat/web/reports/static/reports/app/services/groupsService.js
@@ -26,7 +26,7 @@
  * 	The datastore for the reports app, including data fetched from the
  * 	server and cross-component app state
  */
-angular.module('reportApp').factory('GroupsService', ['$http', function($http){
+angular.module('reportApp').factory('GroupsService', ['reportFactory', function(reportFactory){
 	let groupsServiceInstance = {};
 	// experiments of reports are in arbitrary groups,
 	// in a many-to-many relationship
@@ -273,6 +273,17 @@ angular.module('reportApp').factory('GroupsService', ['$http', function($http){
 		return groupData.find(g => g.reportItems.find(i => i.id === id));
 	};
 
+	groupsServiceInstance.urlBase = '';
+	groupsServiceInstance.saveUrlBase = (url) => {
+		console.log(url);
+		groupsServiceInstance.urlBase = url;
+	};
+
+	groupsServiceInstance.compileRST = (raw) => {
+		let url = `${groupsServiceInstance.urlBase}/rst/`;
+		return reportFactory.compileRST(url, raw);
+	};
+
 	// helper to assert that a group exists
 	function checkForGroup (groupName) {
 		if(!groupData.find(g => g.name === groupName)){
diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html
index a1beb4668de094f58cbab2edf38ff9f3ff28c755..5db1915161da0eca3040eb75deb727be49b51a5c 100644
--- a/beat/web/reports/templates/reports/report.html
+++ b/beat/web/reports/templates/reports/report.html
@@ -42,8 +42,6 @@
     <link rel="stylesheet" href="{% fingerprint "chosen-bootstrap/chosen.bootstrap.min.css" %}" type="text/css" media="screen" />
     <link rel="stylesheet" href="{% fingerprint "datatables/media/css/dataTables.bootstrap.min.css" %}" type="text/css" media="screen" />
     <link rel="stylesheet" href="{% fingerprint "jquery-ui/themes/base/minified/jquery-ui.min.css" %}" type="text/css" media="screen" />
-    <link href="//cdn.quilljs.com/1.2.2/quill.snow.css" rel="stylesheet">
-    <link href="//cdn.quilljs.com/1.2.2/quill.bubble.css" rel="stylesheet">
     {% code_editor_css %}
 {% endblock %}
 
@@ -55,7 +53,6 @@
     <script src="{% fingerprint "chosen/chosen.jquery.min.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "datatables/media/js/jquery.dataTables.min.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "datatables/media/js/dataTables.bootstrap.min.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="//cdn.quilljs.com/1.2.2/quill.min.js"></script>
 
     <!-- Use Google's CDN for angular-js with a local fallback -->
     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
@@ -70,6 +67,7 @@
     <script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.mouse.min.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.sortable.min.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "angular-ui-sortable/sortable.min.js" %}" type="text/javascript" charset="utf-8"></script>
+    <script src="{% fingerprint "angular-ui-codemirror/ui-codemirror.min.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <script src="{% fingerprint "experiments/js/utils.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/js/base_64_encoder_decoder.js" %}" type="text/javascript" charset="utf-8"></script>
diff --git a/buildout.cfg b/buildout.cfg
index 74df35d3f5692d3a80575e3cd644eb2c9984e260..768a3401e470ca02c6658b79210869d3c7bfeda4 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -129,6 +129,7 @@ packages = jquery#~1.11.3
            underscore#~1.8.3
            datatables#~1.10.10
            angular-ui-sortable#~0.14
+	   angular-ui-codemirror
 executable = ${buildout:bin-directory}/bower
 base-directory = beat/web
 downloads = static