diff --git a/app/main/forms.py b/app/main/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..e542e5b207f26b4dcc0561928f57efc28d864f5e
--- /dev/null
+++ b/app/main/forms.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+"""
+app.main.forms
+~~~~~~~~~~~~~~
+
+This module defines the main forms.
+
+:copyright: (c) 2017 European Spallation Source ERIC
+:license: BSD 2-Clause, see LICENSE for more details.
+
+"""
+from flask_wtf import FlaskForm
+from wtforms import SelectField, StringField, validators
+from .. import utils
+
+
+class QRCodeForm(FlaskForm):
+    kind = SelectField('Kind')
+    name = StringField('name', validators=[validators.DataRequired()])
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.kind.choices = utils.get_choices(
+            ('Manufacturer', 'Model', 'Location')
+        )
diff --git a/app/main/views.py b/app/main/views.py
index fecefe0ddd656d750727048a9acb03c79c46eadd..f5f77c65cb5b683a6771d623781094296cd26ced 100644
--- a/app/main/views.py
+++ b/app/main/views.py
@@ -9,10 +9,14 @@ This module implements the main blueprint.
 :license: BSD 2-Clause, see LICENSE for more details.
 
 """
-from flask import Blueprint, render_template, jsonify
+import sqlalchemy as sa
+from flask import (Blueprint, render_template, jsonify,
+                   redirect, url_for, request, flash)
 from flask_login import login_required
+from .forms import QRCodeForm
 from ..extensions import db
 from ..models import Action, Manufacturer, Model, Location, Status, Item
+from ..decorators import login_groups_accepted
 from .. import utils
 
 bp = Blueprint('main', __name__)
@@ -70,3 +74,33 @@ def qrcodes():
                   for item in items]
         codes[model.__name__] = images
     return render_template('qrcodes.html', codes=codes)
+
+
+@bp.route('/qrcodes/create', methods=('GET', 'POST'))
+@login_groups_accepted('admin', 'create')
+def create_qrcodes():
+    kind = request.args.get('kind', 'Manufacturer')
+    form = QRCodeForm(request.form, kind=kind)
+    if form.validate_on_submit():
+        model = globals()[form.kind.data]
+        new_model = model(name=form.name.data)
+        db.session.add(new_model)
+        try:
+            db.session.commit()
+        except sa.exc.IntegrityError as e:
+            db.session.rollback()
+            flash(f'{form.name.data} already exists! Item not created.', 'error')
+        return redirect(url_for('main.create_qrcodes', kind=form.kind.data))
+    return render_template('create_qrcodes.html', form=form)
+
+
+@bp.route('/_retrieve_qrcodes_name/<kind>')
+@login_required
+def retrieve_qrcodes_name(kind):
+    try:
+        model = globals()[kind]
+    except KeyError:
+        raise utils.InventoryError(f"Unknown model '{kind}'", status_code=422)
+    items = db.session.query(model).order_by(model.name)
+    data = [[item.name] for item in items]
+    return jsonify(data=data)
diff --git a/app/static/js/qrcodes.js b/app/static/js/qrcodes.js
new file mode 100644
index 0000000000000000000000000000000000000000..0102a00c83e739993659de4d2d21aeb59a645241
--- /dev/null
+++ b/app/static/js/qrcodes.js
@@ -0,0 +1,19 @@
+$(document).ready(function() {
+
+  var qrcodes_table =  $("#qrcodes_table").DataTable({
+    "ajax": function(data, callback, settings) {
+      var kind = $("#kind").val();
+      $.getJSON(
+        $SCRIPT_ROOT + "/_retrieve_qrcodes_name/" + kind,
+        function(json) {
+          callback(json);
+        });
+    },
+    "paging": false
+  });
+
+  $("#kind").on('change', function() {
+    qrcodes_table.ajax.reload();
+  });
+
+});
diff --git a/app/templates/_helpers.html b/app/templates/_helpers.html
index 006674ca94419e067c552db75d674855acc57636..9580474f148e848c957a12da7e69ad50d059551f 100644
--- a/app/templates/_helpers.html
+++ b/app/templates/_helpers.html
@@ -1,3 +1,3 @@
-{% macro nav_a(active) -%}
-<a class="nav-item nav-link{% if active %} active{% endif %}"
+{% macro is_active(active) -%}
+{% if active %}active{% endif %}
 {%- endmacro %}
diff --git a/app/templates/base.html b/app/templates/base.html
index 127344de748e6242be5df6fd12b5942803abaebf..50c2ff0290a6e3d2dc1cfc7c5fa76d61210e835b 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -1,6 +1,6 @@
 {%- extends "bootstrap/base.html" %}
 {% import "bootstrap/utils.html" as utils %}
-{% from "_helpers.html" import nav_a %}
+{% from "_helpers.html" import is_active %}
 
 {% block styles %}
   {{super()}}
@@ -23,15 +23,23 @@
       <div class="collapse navbar-collapse" id="navbarSupportedContent">
         <div class="navbar-nav mr-auto">
           {% set path = request.path %}
-          {{ nav_a(path in ("/", "/index", "/index/")) }} href="{{ url_for('main.index') }}">Index</a>
-	        {{ nav_a(path == "/qrcodes") }} href="{{ url_for('main.qrcodes') }}">QR Codes</a>
+          <a class="nav-item nav-link {{ is_active(path in ("/", "/index", "/index/")) }}" href="{{ url_for('main.index') }}">Index</a>
+          <div class="dropdown {{ is_active(path.startswith("/qrcodes")) }}">
+            <a class="nav-link dropdown-toggle" href="#" id="qrcodesDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+              QR Codes
+            </a>
+            <div class="dropdown-menu" aria-labelledby="qrcodesDropdownMenuLink">
+              <a class="dropdown-item" href="{{ url_for('main.qrcodes') }}">View</a>
+              <a class="dropdown-item" href="{{ url_for('main.create_qrcodes') }}">Create</a>
+            </div>
+          </div>
           {% if current_user.is_authenticated and current_user.is_admin %}
             <a class="nav-item nav-link" href="{{ url_for('admin.index') }}">Admin</a>
           {% endif %}
         </div>
         <div class="navbar-nav">
           {% if current_user.is_authenticated %}
-          <div class="dropdown">
+          <div class="dropdown {{ is_active(path == "/profile") }}">
             <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
               {{current_user}}
             </a>
diff --git a/app/templates/create_qrcodes.html b/app/templates/create_qrcodes.html
new file mode 100644
index 0000000000000000000000000000000000000000..3fc61b6a4b430bbfc6e0c6c88363831d72a51719
--- /dev/null
+++ b/app/templates/create_qrcodes.html
@@ -0,0 +1,34 @@
+{%- extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
+
+{% block title %}Create QR Codes{% endblock %}
+
+{% block main %}
+  <h2>Create QR Codes</h2>
+
+  <form method="POST" class="form-inline">
+    {{ form.csrf_token }}
+
+    {{ form.kind.label(class="sr-only") | safe }}
+    {{ form.kind(class="form-control mb-2 mr-sm-2 mb-sm-0") }}
+
+    {{ form.name.label(class="sr-only") | safe }}
+    {{ form.name(class="form-control mb-2 mr-sm-2 mb-sm-0", placeholder="name", required=True) }}
+
+    <button type="submit" class="btn btn-primary">Create</button>
+  </form>
+
+  <hr class="separator">
+
+  <table id="qrcodes_table" class="table table-bordered table-hover table-sm">
+    <thead>
+      <tr>
+        <th>Name</th>
+      </tr>
+    </thead>
+  </table>
+{%- endblock %}
+
+{% block inventory_scripts %}
+  <script src="{{ url_for('static', filename='js/qrcodes.js') }}"></script>
+{% endblock %}
diff --git a/app/utils.py b/app/utils.py
index 1ea13faa0fbad4e49cbeb8290779cd24df97cdc8..8365bcf318415c4949ba9ee03b1cafd3e3868f2e 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -92,3 +92,14 @@ def attribute_to_string(value):
         return value[0]
     else:
         return value
+
+
+def get_choices(iterable, allow_blank=False, allow_null=False):
+    """Return a list of (value, label)"""
+    choices = []
+    if allow_blank:
+        choices = [('', '')]
+    if allow_null:
+        choices.append(('null', 'not set'))
+    choices.extend([(val, val) for val in iterable])
+    return choices