diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py
index 935a0bed37..ac6e268f78 100644
--- a/InvenTree/InvenTree/version.py
+++ b/InvenTree/InvenTree/version.py
@@ -12,11 +12,16 @@ import common.models
 INVENTREE_SW_VERSION = "0.6.0 dev"
 
 # InvenTree API version
-INVENTREE_API_VERSION = 17
+INVENTREE_API_VERSION = 18
 
 """
 Increment this API version number whenever there is a significant change to the API that any clients need to know about
 
+v18 -> 2021-11-15
+    - Adds the ability to filter BomItem API by "uses" field
+    - This returns a list of all BomItems which "use" the specified part
+    - Includes inherited BomItem objects
+
 v17 -> 2021-11-09
     - Adds API endpoints for GLOBAL and USER settings objects
     - Ref: https://github.com/inventree/InvenTree/pull/2275
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py
index b08834445c..eeb8ec1255 100644
--- a/InvenTree/part/api.py
+++ b/InvenTree/part/api.py
@@ -832,18 +832,6 @@ class PartList(generics.ListCreateAPIView):
 
         queryset = super().filter_queryset(queryset)
 
-        # Filter by "uses" query - Limit to parts which use the provided part
-        uses = params.get('uses', None)
-
-        if uses:
-            try:
-                uses = Part.objects.get(pk=uses)
-
-                queryset = queryset.filter(uses.get_used_in_filter())
-
-            except (ValueError, Part.DoesNotExist):
-                pass
-
         # Exclude specific part ID values?
         exclude_id = []
 
@@ -1040,13 +1028,19 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
     serializer_class = part_serializers.PartParameterTemplateSerializer
 
     filter_backends = [
+        DjangoFilterBackend,
         filters.OrderingFilter,
+        filters.SearchFilter,
     ]
 
     filter_fields = [
         'name',
     ]
 
+    search_fields = [
+        'name',
+    ]
+
 
 class PartParameterList(generics.ListCreateAPIView):
     """ API endpoint for accessing a list of PartParameter objects
@@ -1211,6 +1205,54 @@ class BomList(generics.ListCreateAPIView):
             except (ValueError, Part.DoesNotExist):
                 pass
 
+        """
+        Filter by 'uses'?
+
+        Here we pass a part ID and return BOM items for any assemblies which "use" (or "require") that part.
+
+        There are multiple ways that an assembly can "use" a sub-part:
+
+        A) Directly specifying the sub_part in a BomItem field
+        B) Specifing a "template" part with inherited=True
+        C) Allowing variant parts to be substituted
+        D) Allowing direct substitute parts to be specified
+
+        - BOM items which are "inherited" by parts which are variants of the master BomItem
+        """
+        uses = params.get('uses', None)
+
+        if uses is not None:
+
+            try:
+                # Extract the part we are interested in
+                uses_part = Part.objects.get(pk=uses)
+
+                # Construct the database query in multiple parts
+
+                # A) Direct specification of sub_part
+                q_A = Q(sub_part=uses_part)
+
+                # B) BomItem is inherited and points to a "parent" of this part
+                parents = uses_part.get_ancestors(include_self=False)
+
+                q_B = Q(
+                    inherited=True,
+                    sub_part__in=parents
+                )
+
+                # C) Substitution of variant parts
+                # TODO
+
+                # D) Specification of individual substitutes
+                # TODO
+
+                q = q_A | q_B
+
+                queryset = queryset.filter(q)
+
+            except (ValueError, Part.DoesNotExist):
+                pass
+
         if self.include_pricing():
             queryset = self.annotate_pricing(queryset)
 
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html
index 39ed011861..0d05665f7d 100644
--- a/InvenTree/part/templates/part/detail.html
+++ b/InvenTree/part/templates/part/detail.html
@@ -388,9 +388,7 @@
                         {% if part.variant_of %}
                         <li><a class='dropdown-item' href='#' id='bom-duplicate'><span class='fas fa-clone'></span> {% trans "Copy BOM" %}</a></li>
                         {% endif %}
-                        {% if not part.is_bom_valid %}
                         <li><a class='dropdown-item' href='#' id='validate-bom'><span class='fas fa-clipboard-check icon-green'></span> {% trans "Validate BOM" %}</a></li>
-                        {% endif %}
                     </ul>
                 </div>
         
@@ -649,14 +647,10 @@
 
     // Load the "used in" tab
     onPanelLoad("used-in", function() {
-        loadPartTable('#used-table',
-            '{% url "api-part-list" %}',
-            {
-                params: {
-                    uses: {{ part.pk }},
-                },
-                filterTarget: '#filter-list-usedin',
-            }
+
+        loadUsedInTable(
+            '#used-table',
+            {{ part.pk }},
         );
     });
 
diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py
index ec377bd513..b16de1b9d7 100644
--- a/InvenTree/part/test_api.py
+++ b/InvenTree/part/test_api.py
@@ -1123,6 +1123,59 @@ class BomItemTest(InvenTreeAPITestCase):
         response = self.get(url, expected_code=200)
         self.assertEqual(len(response.data), 5)
 
+    def test_bom_item_uses(self):
+        """
+        Tests for the 'uses' field
+        """
+
+        url = reverse('api-bom-list')
+
+        # Test that the direct 'sub_part' association works
+
+        assemblies = []
+
+        for i in range(5):
+            assy = Part.objects.create(
+                name=f"Assy_{i}",
+                description="An assembly made of other parts",
+                active=True,
+                assembly=True
+            )
+
+            assemblies.append(assy)
+
+        components = []
+
+        # Create some sub-components
+        for i in range(5):
+
+            cmp = Part.objects.create(
+                name=f"Component_{i}",
+                description="A sub component",
+                active=True,
+                component=True
+            )
+
+            for j in range(i):
+                # Create a BOM item
+                BomItem.objects.create(
+                    quantity=10,
+                    part=assemblies[j],
+                    sub_part=cmp,
+                )
+
+            components.append(cmp)
+
+            response = self.get(
+                url,
+                {
+                    'uses': cmp.pk,
+                },
+                expected_code=200,
+            )
+
+            self.assertEqual(len(response.data), i)
+
 
 class PartParameterTest(InvenTreeAPITestCase):
     """
diff --git a/InvenTree/templates/js/translated/api.js b/InvenTree/templates/js/translated/api.js
index 15a74a9a71..735ce0a676 100644
--- a/InvenTree/templates/js/translated/api.js
+++ b/InvenTree/templates/js/translated/api.js
@@ -217,8 +217,10 @@ function showApiError(xhr, url) {
         break;
     }
 
-    message += '<hr>';
-    message += `URL: ${url}`;
+    if (url) {
+        message += '<hr>';
+        message += `URL: ${url}`;
+    }
 
     showMessage(title, {
         style: 'danger',
diff --git a/InvenTree/templates/js/translated/bom.js b/InvenTree/templates/js/translated/bom.js
index ee04cb8660..1885624dd8 100644
--- a/InvenTree/templates/js/translated/bom.js
+++ b/InvenTree/templates/js/translated/bom.js
@@ -16,6 +16,7 @@
 /* exported
     newPartFromBomWizard,
     loadBomTable,
+    loadUsedInTable,
     removeRowFromBomWizard,
     removeColFromBomWizard,
 */
@@ -311,7 +312,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
 }
 
 
-function loadBomTable(table, options) {
+function loadBomTable(table, options={}) {
     /* Load a BOM table with some configurable options.
      * 
      * Following options are available:
@@ -395,7 +396,7 @@ function loadBomTable(table, options) {
 
                 var sub_part = row.sub_part_detail;
 
-                html += makePartIcons(row.sub_part_detail);
+                html += makePartIcons(sub_part);
 
                 if (row.substitutes && row.substitutes.length > 0) {
                     html += makeIconBadge('fa-exchange-alt', '{% trans "Substitutes Available" %}');
@@ -672,8 +673,9 @@ function loadBomTable(table, options) {
 
                     table.treegrid('collapseAll');
                 },
-                error: function() {
+                error: function(xhr) {
                     console.log('Error requesting BOM for part=' + part_pk);
+                    showApiError(xhr);
                 }
             }
         );
@@ -835,3 +837,166 @@ function loadBomTable(table, options) {
         });
     }
 }
+
+
+/*
+ * Load a table which shows the assemblies which "require" a certain part.
+ *
+ * Arguments:
+ * - table: The ID string of the table element e.g. '#used-in-table'
+ * - part_id: The ID (PK) of the part we are interested in
+ * 
+ * Options:
+ * - 
+ * 
+ * The following "options" are available.
+ */
+function loadUsedInTable(table, part_id, options={}) {
+
+    var params = options.params || {};
+
+    params.uses = part_id;
+    params.part_detail = true;
+    params.sub_part_detail = true,
+    params.show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
+
+    var filters = {};
+
+    if (!options.disableFilters) {
+        filters = loadTableFilters('usedin');
+    }
+
+    for (var key in params) {
+        filters[key] = params[key];
+    }
+
+    setupFilterList('usedin', $(table), options.filterTarget || '#filter-list-usedin');
+
+    function loadVariantData(row) {
+        // Load variants information for inherited BOM rows
+
+        inventreeGet(
+            '{% url "api-part-list" %}',
+            {
+                assembly: true,
+                ancestor: row.part,
+            },
+            {
+                success: function(variantData) {
+                    // Iterate through each variant item
+                    for (var jj = 0; jj < variantData.length; jj++) {
+                        variantData[jj].parent = row.pk;
+                        
+                        var variant = variantData[jj];
+
+                        // Add this variant to the table, augmented
+                        $(table).bootstrapTable('append', [{
+                            // Point the parent to the "master" assembly row 
+                            parent: row.pk,
+                            part: variant.pk,                       
+                            part_detail: variant,
+                            sub_part: row.sub_part,
+                            sub_part_detail: row.sub_part_detail,
+                            quantity: row.quantity,
+                        }]);
+                    }
+                },
+                error: function(xhr) {
+                    showApiError(xhr);
+                }
+            }
+        );
+    }
+
+    $(table).inventreeTable({
+        url: options.url || '{% url "api-bom-list" %}',
+        name: options.table_name || 'usedin',
+        sortable: true,
+        search: true,
+        showColumns: true,
+        queryParams: filters,
+        original: params,
+        rootParentId: 'top-level-item',
+        idField: 'pk',
+        uniqueId: 'pk',
+        parentIdField: 'parent',
+        treeShowField: 'part',
+        onLoadSuccess: function(tableData) {
+            // Once the initial data are loaded, check if there are any "inherited" BOM lines
+            for (var ii = 0; ii < tableData.length; ii++) {
+                var row = tableData[ii];
+
+                // This is a "top level" item in the table
+                row.parent = 'top-level-item';
+
+                // Ignore this row as it is not "inherited" by variant parts
+                if (!row.inherited) {
+                    continue;
+                }
+
+                loadVariantData(row);
+            }
+        },
+        onPostBody: function() {
+            $(table).treegrid({
+                treeColumn: 0,
+            });
+        },
+        columns: [
+            {
+                field: 'pk',
+                title: 'ID',
+                visible: false,
+                switchable: false,
+            },
+            {
+                field: 'part',
+                title: '{% trans "Assembly" %}',
+                switchable: false,
+                sortable: true,
+                formatter: function(value, row) {
+                    var url = `/part/${value}/?display=bom`;
+                    var html = '';
+
+                    var part = row.part_detail;
+
+                    html += imageHoverIcon(part.thumbnail);
+                    html += renderLink(part.full_name, url);
+                    html += makePartIcons(part);
+
+                    return html;
+                }
+            },
+            {
+                field: 'sub_part',
+                title: '{% trans "Required Part" %}',
+                sortable: true,
+                formatter: function(value, row) {
+                    var url = `/part/${value}/`;
+                    var html = '';
+
+                    var sub_part = row.sub_part_detail;
+
+                    html += imageHoverIcon(sub_part.thumbnail);
+                    html += renderLink(sub_part.full_name, url);
+                    html += makePartIcons(sub_part);
+
+                    return html;
+                }
+            },
+            {
+                field: 'quantity',
+                title: '{% trans "Required Quantity" %}',
+                formatter: function(value, row) {
+                    var html = value;
+
+                    if (row.parent && row.parent != 'top-level-item') {
+                        html += ` <em>({% trans "Inherited from parent BOM" %})</em>`;
+                    }
+
+                    return html;
+                }
+            }
+        ]
+    });
+}
diff --git a/InvenTree/templates/js/translated/filters.js b/InvenTree/templates/js/translated/filters.js
index 4383f0a096..227fbb8009 100644
--- a/InvenTree/templates/js/translated/filters.js
+++ b/InvenTree/templates/js/translated/filters.js
@@ -281,23 +281,24 @@ function setupFilterList(tableKey, table, target) {
     // One blank slate, please
     element.empty();
 
-    element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`);
+    var buttons = '';
 
-    // Callback for reloading the table
-    element.find(`#reload-${tableKey}`).click(function() {
-        $(table).bootstrapTable('refresh');
-    });
+    buttons += `<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`;
 
-    // If there are no filters defined for this table, exit now
-    if (jQuery.isEmptyObject(getAvailableTableFilters(tableKey))) {
-        return;
+    // If there are filters defined for this table, add more buttons
+    if (!jQuery.isEmptyObject(getAvailableTableFilters(tableKey))) {
+        buttons += `<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-filter'></span></button>`;
+
+        if (Object.keys(filters).length > 0) {
+            buttons += `<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`;
+        }
     }
 
-    element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-filter'></span></button>`);
-
-    if (Object.keys(filters).length > 0) {
-        element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`);
-    }
+    element.html(`
+    <div class='btn-group' role='group'>
+        ${buttons}
+    </div>
+    `);
 
     for (var key in filters) {
         var value = getFilterOptionValue(tableKey, key, filters[key]);
@@ -307,6 +308,11 @@ function setupFilterList(tableKey, table, target) {
         element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
     }
 
+    // Callback for reloading the table
+    element.find(`#reload-${tableKey}`).click(function() {
+        $(table).bootstrapTable('refresh');
+    });
+
     // Add a callback for adding a new filter
     element.find(`#${add}`).click(function clicked() {
 
@@ -316,10 +322,12 @@ function setupFilterList(tableKey, table, target) {
 
             var html = '';
 
+            html += `<div class='input-group'>`;
             html += generateAvailableFilterList(tableKey);
             html += generateFilterInput(tableKey);
 
             html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-button' id='${make}'><span class='fas fa-plus'></span></button>`;
+            html += `</div>`;
 
             element.append(html);
 
diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js
index 2f25fef259..fd1668cc77 100644
--- a/InvenTree/templates/js/translated/forms.js
+++ b/InvenTree/templates/js/translated/forms.js
@@ -924,8 +924,8 @@ function handleFormSuccess(response, options) {
     var cache = (options.follow && response.url) || options.redirect || options.reload;
 
     // Display any messages
-    if (response && response.success) {
-        showAlertOrCache(response.success, cache, {style: 'success'});
+    if (response && (response.success || options.successMessage)) {
+        showAlertOrCache(response.success || options.successMessage, cache, {style: 'success'});
     }
     
     if (response && response.info) {
diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js
index dc1adf8837..89e09a314e 100644
--- a/InvenTree/templates/js/translated/part.js
+++ b/InvenTree/templates/js/translated/part.js
@@ -331,6 +331,7 @@ function editPart(pk) {
         groups: groups,
         title: '{% trans "Edit Part" %}',
         reload: true,
+        successMessage: '{% trans "Part edited" %}',
     });
 }
 
diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js
index 537adefee9..903774f8e5 100644
--- a/InvenTree/templates/js/translated/table_filters.js
+++ b/InvenTree/templates/js/translated/table_filters.js
@@ -77,10 +77,22 @@ function getAvailableTableFilters(tableKey) {
     // Filters for the "used in" table
     if (tableKey == 'usedin') {
         return {
+            'inherited': {
+                type: 'bool',
+                title: '{% trans "Inherited" %}',
+            },
+            'optional': {
+                type: 'bool',
+                title: '{% trans "Optional" %}',
+            },
             'part_active': {
                 type: 'bool',
                 title: '{% trans "Active" %}',
             },
+            'part_trackable': {
+                type: 'bool',
+                title: '{% trans "Trackable" %}',
+            },
         };
     }
 
diff --git a/InvenTree/templates/search_form.html b/InvenTree/templates/search_form.html
index f3928888b5..f77d1ccf7b 100644
--- a/InvenTree/templates/search_form.html
+++ b/InvenTree/templates/search_form.html
@@ -2,8 +2,10 @@
 
 <form class="d-flex" action="{% url 'search' %}" method='post'>
     {% csrf_token %}
-    <input type="text" name='search' class="form-control" aria-label='{% trans "Search" %}' id="search-bar" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
-    <button type="submit" id='search-submit' class="btn btn-secondary" title='{% trans "Search" %}'>
-        <span class='fas fa-search'></span>
-    </button>
+    <div class='input-group'>
+        <input type="text" name='search' class="form-control" aria-label='{% trans "Search" %}' id="search-bar" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
+        <button type="submit" id='search-submit' class="btn btn-secondary" title='{% trans "Search" %}'>
+            <span class='fas fa-search'></span>
+        </button>
+    </div>
 </form>