diff --git a/.jshintrc b/.jshintrc
index 836165107b094add85329282f9d4353e24288727..5fec253525d7255e86fd5584711d3857cdf694c0 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -3,6 +3,7 @@
   "globals": {
       "angular": false,
       "console": false,
+      "React": false,
       "Set": false
   }
 }
diff --git a/circle.yml b/circle.yml
index 2ece3029d06d2b84c00bd4709cccb47092024360..032e020958650c9221a14a6fd89f191081ee7aa4 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,5 +4,5 @@ machine:
       oraclejdk8
 test:
   override:
-    - case $CIRCLE_NODE_INDEX in 0) lein eastwood ;; 1) lein test ;; 2) lein uberjar ;; 3) ./util/lint_js.sh && lein bikeshed --max-line-length 240 ;; esac:
+    - case $CIRCLE_NODE_INDEX in 0) lein eastwood ;; 1) lein test ;; 2) lein uberjar ;; 3) ./lint_js.sh && lein bikeshed --max-line-length 240 ;; esac:
         parallel: true
diff --git a/util/lint_js.sh b/lint_js.sh
similarity index 91%
rename from util/lint_js.sh
rename to lint_js.sh
index 7e2572ce00dd00bc4473c215ea33504fd53dd419..77c55be5d32d9c6248caf2cbfeb2540a9bad85f8 100755
--- a/util/lint_js.sh
+++ b/lint_js.sh
@@ -2,9 +2,9 @@
 
 # Simple shell script for running jshint and nicely formatting the output
 
-JS_HINT=./node_modules/jshint/bin/jshint
+JS_HINT=./node_modules/jsxhint/cli.js
 JS_HINT_OPTS='--config .jshintrc'
-JS_FILES=`find resources/frontend_client/app -name "*.js" | grep -v bower_components | grep -v 'app/test/' | grep -v '\#' | grep -v 'app/dist/' | grep -v react`
+JS_FILES=`find resources/frontend_client/app -name "*.js*" | grep -v bower_components | grep -v 'app/test/' | grep -v '\#' | grep -v 'app/dist/'`
 
 BOLD='\033[1;30m'
 RED='\033[0;31m' # \e doesn't work on OS X but \033 works on either
diff --git a/package.json b/package.json
index b2b133457e1ecc31ffc7cc65f4f8eb18651800a5..1ae2d45295baf1af193a9d009334e91d589e320e 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     },
     "devDependencies": {
         "http-server": "^0.6.1",
-        "jshint": "2.6.3",
+        "jsxhint": "0.13.3",
         "karma": "~0.10",
         "karma-junit-reporter": "^0.2.2",
         "protractor": "~0.20.1",
diff --git a/resources/frontend_client/app/query_builder/database_selector.react.js b/resources/frontend_client/app/query_builder/database_selector.react.js
index 4beae68faa30d2fe8f891da84b80d27f6beb30a8..8509c7629b1f0f6b65f7d9d60b27927e4cd57ded 100644
--- a/resources/frontend_client/app/query_builder/database_selector.react.js
+++ b/resources/frontend_client/app/query_builder/database_selector.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global SelectionModule*/
 
 var DatabaseSelector = React.createClass({
     displayName: 'DatabaseSelector',
diff --git a/resources/frontend_client/app/query_builder/date_filter.react.js b/resources/frontend_client/app/query_builder/date_filter.react.js
index 6bd6dbd126f2a20727615d4efdf9d9c0bfae1e16..cfcbe9f451efb6550e3e8912b8e6f98a32c6be53 100644
--- a/resources/frontend_client/app/query_builder/date_filter.react.js
+++ b/resources/frontend_client/app/query_builder/date_filter.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global moment, DatePicker*/
 
 var DateFilter = React.createClass({
     displayName: 'DateFilter',
diff --git a/resources/frontend_client/app/query_builder/filter_widget.react.js b/resources/frontend_client/app/query_builder/filter_widget.react.js
index cb582a385166a46e65d4423dffcd6db062660260..30461a3664f23442e5b49e78f563cfe0e0c38a0b 100644
--- a/resources/frontend_client/app/query_builder/filter_widget.react.js
+++ b/resources/frontend_client/app/query_builder/filter_widget.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global DateFilter, SelectionModule*/
 
 var FilterWidget = React.createClass({
     displayName: 'FilterWidget',
@@ -18,7 +19,7 @@ var FilterWidget = React.createClass({
         this.props.updateFilter(value, 2, index);
     },
     _isOpen: function (value) {
-        if(value != undefined) {
+        if (value !== undefined) {
             return true;
         } else {
             return false;
@@ -56,7 +57,7 @@ var FilterWidget = React.createClass({
                     parentIndex={this.props.index}
                 />
             </div>
-        )
+        );
     },
     _getSafeValues: function () {
         return this.props.valueFields.values.map(function(value) {
@@ -106,7 +107,7 @@ var FilterWidget = React.createClass({
                                             date.format('YYYY-MM-DD'),
                                             2,
                                             this.props.index
-                                        )
+                                        );
                                     }.bind(this)
                                 }
                             />
diff --git a/resources/frontend_client/app/query_builder/header.react.js b/resources/frontend_client/app/query_builder/header.react.js
index d61d09aa13cc0221ef8c5bb251e4c27f0f67ba71..7f9d41b3fa746c254fc0f17760df44de811ef17d 100644
--- a/resources/frontend_client/app/query_builder/header.react.js
+++ b/resources/frontend_client/app/query_builder/header.react.js
@@ -12,6 +12,6 @@ var QueryHeader = React.createClass({
         var name = this.props.name || "What would you like to know?";
         return (
             <h1 className="QueryName">{name}</h1>
-        )
+        );
     }
 });
diff --git a/resources/frontend_client/app/query_builder/query_picker.react.js b/resources/frontend_client/app/query_builder/query_picker.react.js
index 0e9e4a8ce473cd304420b73fffa36c8cca356398..fe36e4d51e66fafc2b5ef94ed490429fa58e8d39 100644
--- a/resources/frontend_client/app/query_builder/query_picker.react.js
+++ b/resources/frontend_client/app/query_builder/query_picker.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global SelectionModule, DatabaseSelector*/
 
 var QueryPicker = React.createClass({
     displayName: 'QueryPicker',
@@ -43,7 +44,7 @@ var QueryPicker = React.createClass({
 
         if(this.props.aggregationComplete() && this.props.options.breakout_options.fields.length > 0) {
 
-            (this.props.query.breakout.length < 1) ? addDimensionButtonText = "Grouped by" : addDimensionButtonText = "and";
+            addDimensionButtonText = (this.props.query.breakout.length < 1) ? "Grouped by" : "and";
 
             if(this.props.query.breakout.length < 2) {
                 addDimensionButton = (
@@ -104,7 +105,7 @@ var QueryPicker = React.createClass({
             // if there's a value in the second aggregation slot
             if(this.props.query.aggregation.length > 1) {
                 if(this.props.query.aggregation[1] !== null) {
-                    aggregationTargetListOpen = false
+                    aggregationTargetListOpen = false;
                 }
                 aggregationTargetHtml = (
                     <SelectionModule
diff --git a/resources/frontend_client/app/query_builder/saver.react.js b/resources/frontend_client/app/query_builder/saver.react.js
index e62b0fab055950dc7e0c5118d9c70884513c93f0..e10192c5d28f6a91b1cee384468da1b11be52f74 100644
--- a/resources/frontend_client/app/query_builder/saver.react.js
+++ b/resources/frontend_client/app/query_builder/saver.react.js
@@ -1,3 +1,6 @@
+'use strict';
+/*global cx, OnClickOutside, SelectionModule*/
+
 var Saver = React.createClass({
     displayName: 'Saver',
     propTypes: {
@@ -16,7 +19,7 @@ var Saver = React.createClass({
         };
     },
     handleClickOutside: function () {
-        this.replaceState(this.getInitialState())
+        this.replaceState(this.getInitialState());
     },
     _openModal: function () {
         this.setState({
@@ -24,22 +27,22 @@ var Saver = React.createClass({
             triggerAction: this._save
         }, function () {
             // focus the name field
-            this.refs.name.getDOMNode().focus()
-        })
+            this.refs.name.getDOMNode().focus();
+        });
     },
     _save: function () {
         var name = this.refs.name.getDOMNode().value,
-            description = this.refs.description.getDOMNode().value
+            description = this.refs.description.getDOMNode().value;
 
         this.props.save({
             name: name,
             description: description
-        })
+        });
         // reset the modal
         this.setState({
             modalOpen: false,
             triggerAction: this._openModal
-        })
+        });
     },
     render: function () {
         var buttonClasses = cx({
@@ -47,19 +50,19 @@ var Saver = React.createClass({
             'Button': true,
             'block': true,
             'Button--primary': this.state.modalOpen
-        })
+        });
         var modalClasses = cx({
             'SaveModal': true,
             'Modal--showing': this.state.modalOpen
-        })
+        });
 
         var buttonText;
 
         // if the query has changed or the modal has been opened
-        if(this.props.hasChanged == true || this.state.modalOpen == true) {
-            buttonText = "Save"
+        if (this.props.hasChanged === true || this.state.modalOpen === true) {
+            buttonText = "Save";
         } else {
-            buttonText = "Edit"
+            buttonText = "Edit";
         }
 
         var privacyOptions = [
@@ -102,6 +105,6 @@ var Saver = React.createClass({
                     {buttonText}
                 </a>
             </div>
-        )
+        );
     }
 });
diff --git a/resources/frontend_client/app/query_builder/selection_module.react.js b/resources/frontend_client/app/query_builder/selection_module.react.js
index e1e9cb2bd6271e5b967e9c45678424b65f149267..3bb0c917f578ea09f763618d6c8fa4e8b6f2f2e4 100644
--- a/resources/frontend_client/app/query_builder/selection_module.react.js
+++ b/resources/frontend_client/app/query_builder/selection_module.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global cx, OnClickOutside, SearchBar*/
 
 var SelectionModule = React.createClass({
     displayName:'SelectionModule',
@@ -23,7 +24,7 @@ var SelectionModule = React.createClass({
             searchThreshold: 20,
             searchEnabled: false,
             filterTerm: null
-        }
+        };
     },
     handleClickOutside: function () {
         this.setState({
@@ -84,9 +85,9 @@ var SelectionModule = React.createClass({
     _select: function (item) {
         var index = this.props.index;
         // send back the item with the specified action
-        if(this.props.action) {
-            if(index != undefined) {
-                if(this.props.parentIndex) {
+        if (this.props.action) {
+            if (index !== undefined) {
+                if (this.props.parentIndex) {
                     this.props.action(item[this.props.selectedKey], index, this.props.parentIndex);
                 } else {
                     this.props.action(item[this.props.selectedKey], index);
@@ -127,13 +128,13 @@ var SelectionModule = React.createClass({
         });
 
         if(this._enableSearch()) {
-            searchBar = <SearchBar onFilter={this._filterSelections} />
+            searchBar = <SearchBar onFilter={this._filterSelections} />;
         }
 
         if(this.props.remove) {
             var style = {
                 fill: '#ddd'
-            }
+            };
             remove = (
                 <a className="RemoveTrigger" href="#" onClick={this.props.remove.bind(null, this.props.index)}>
                     <svg className="geomicon" data-icon="close" viewBox="0 0 32 32" style={style} width="16px" height="16px">
diff --git a/resources/frontend_client/app/query_builder/visualization.react.js b/resources/frontend_client/app/query_builder/visualization.react.js
index 609cda9872ffb900dac564564d4a876cd36d6cdb..a43ad9534ac01bcec78735962602bd268b0792a8 100644
--- a/resources/frontend_client/app/query_builder/visualization.react.js
+++ b/resources/frontend_client/app/query_builder/visualization.react.js
@@ -1,4 +1,5 @@
 'use strict';
+/*global cx, CardRenderer*/
 
 var QueryVisualization = React.createClass({
     displayName: 'QueryVisualization',
@@ -40,7 +41,7 @@ var QueryVisualization = React.createClass({
             if(this.state.type === 'table') {
                 tableRows = this.props.result.data.rows.map(function (row) {
                     var rowCols = row.map(function (data) {
-                        return (<td>{data.toString()}</td>)
+                        return (<td>{data.toString()}</td>);
                     });
 
                     return (<tr>{rowCols}</tr>);
@@ -98,7 +99,7 @@ var QueryVisualization = React.createClass({
                 var buttonClasses = cx({
                     'Button': true,
                     'Button--primary' : (type == this.state.type)
-                })
+                });
                 return (
                     <a className={buttonClasses} href="#" onClick={this._changeType.bind(null, type)}>{type}</a>
                 );