diff --git a/common/jobs/job_export_sch_bom.h b/common/jobs/job_export_sch_bom.h
index 4465d6e917..6dd338e0ee 100644
--- a/common/jobs/job_export_sch_bom.h
+++ b/common/jobs/job_export_sch_bom.h
@@ -35,6 +35,10 @@ public:
     wxString m_filename;
     wxString m_outputFile;
 
+    // Preset options (from schematic)
+    wxString m_bomPresetName;
+    wxString m_bomFmtPresetName;
+
     // Format options
     wxString m_fieldDelimiter;
     wxString m_stringDelimiter;
diff --git a/common/settings/bom_settings.cpp b/common/settings/bom_settings.cpp
index 068bea7d84..9ca939368d 100644
--- a/common/settings/bom_settings.cpp
+++ b/common/settings/bom_settings.cpp
@@ -170,6 +170,12 @@ BOM_PRESET BOM_PRESET::Attributes()
     return p;
 }
 
+std::vector<BOM_PRESET> BOM_PRESET::BuiltInPresets()
+{
+    return { BOM_PRESET::GroupedByValue(), BOM_PRESET::GroupedByValueFootprint(),
+             BOM_PRESET::Attributes() };
+}
+
 //Implementations for BOM_FMT_PRESET
 bool BOM_FMT_PRESET::operator==( const BOM_FMT_PRESET& rhs ) const
 {
@@ -233,3 +239,8 @@ BOM_FMT_PRESET BOM_FMT_PRESET::Semicolons()
         _HKI( "Semicolons" ), true, wxS( ";" ), wxT( "'" ), wxT( "," ), wxT( "" ), false, false
     };
 }
+
+std::vector<BOM_FMT_PRESET> BOM_FMT_PRESET::BuiltInPresets()
+{
+    return { BOM_FMT_PRESET::CSV(), BOM_FMT_PRESET::TSV(), BOM_FMT_PRESET::Semicolons() };
+}
diff --git a/eeschema/dialogs/dialog_symbol_fields_table.cpp b/eeschema/dialogs/dialog_symbol_fields_table.cpp
index b04923ac66..ca22c80367 100644
--- a/eeschema/dialogs/dialog_symbol_fields_table.cpp
+++ b/eeschema/dialogs/dialog_symbol_fields_table.cpp
@@ -1387,9 +1387,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::loadDefaultBomPresets()
     m_bomPresetMRU.clear();
 
     // Load the read-only defaults
-    for( const BOM_PRESET& preset : { BOM_PRESET::GroupedByValue(),
-                                      BOM_PRESET::GroupedByValueFootprint(),
-                                      BOM_PRESET::Attributes() } )
+    for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
     {
         m_bomPresets[preset.name] = preset;
         m_bomPresets[preset.name].readOnly = true;
@@ -1805,8 +1803,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::loadDefaultBomFmtPresets()
     m_bomFmtPresetMRU.clear();
 
     // Load the read-only defaults
-    for( const BOM_FMT_PRESET& preset :
-         { BOM_FMT_PRESET::CSV(), BOM_FMT_PRESET::TSV(), BOM_FMT_PRESET::Semicolons() } )
+    for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
     {
         m_bomFmtPresets[preset.name] = preset;
         m_bomFmtPresets[preset.name].readOnly = true;
diff --git a/eeschema/eeschema_jobs_handler.cpp b/eeschema/eeschema_jobs_handler.cpp
index 533ab3823e..d67e788984 100644
--- a/eeschema/eeschema_jobs_handler.cpp
+++ b/eeschema/eeschema_jobs_handler.cpp
@@ -343,30 +343,63 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
 
     BOM_PRESET preset;
 
-    size_t i = 0;
-    for( wxString fieldName : aBomJob->m_fieldsOrdered )
+    // Load a preset if one is specified
+    if( !aBomJob->m_bomPresetName.IsEmpty() )
     {
-        struct BOM_FIELD field;
+        // Make sure the built-in presets are loaded
+        for( const BOM_PRESET& p : BOM_PRESET::BuiltInPresets() )
+            sch->Settings().m_BomPresets.emplace_back( p );
 
-        field.name = fieldName;
-        field.show = true;
-        field.groupBy = std::find( aBomJob->m_fieldsGroupBy.begin(), aBomJob->m_fieldsGroupBy.end(),
-                                   field.name )
-                        != aBomJob->m_fieldsGroupBy.end();
-        field.label =
-                ( ( aBomJob->m_fieldsLabels.size() > i ) && !aBomJob->m_fieldsLabels[i].IsEmpty() )
-                        ? aBomJob->m_fieldsLabels[i]
-                        : field.name;
+        // Find the preset
+        BOM_PRESET* schPreset = nullptr;
 
-        preset.fieldsOrdered.emplace_back( field );
-        i++;
+        for( BOM_PRESET& p : sch->Settings().m_BomPresets )
+        {
+            if( p.name == aBomJob->m_bomPresetName )
+            {
+                schPreset = &p;
+                break;
+            }
+        }
+
+        if( !schPreset )
+        {
+            m_reporter->Report(
+                    wxString::Format( _( "BOM preset '%s' not found" ), aBomJob->m_bomPresetName ),
+                    RPT_SEVERITY_ERROR );
+
+            return CLI::EXIT_CODES::ERR_UNKNOWN;
+        }
+
+        preset = *schPreset;
     }
+    else
+    {
+        size_t i = 0;
+        for( wxString fieldName : aBomJob->m_fieldsOrdered )
+        {
+            struct BOM_FIELD field;
 
-    preset.sortAsc = aBomJob->m_sortAsc;
-    preset.sortField = aBomJob->m_sortField;
-    preset.filterString = aBomJob->m_filterString;
-    preset.groupSymbols = ( aBomJob->m_fieldsGroupBy.size() > 0 );
-    preset.excludeDNP = aBomJob->m_excludeDNP;
+            field.name = fieldName;
+            field.show = true;
+            field.groupBy = std::find( aBomJob->m_fieldsGroupBy.begin(),
+                                       aBomJob->m_fieldsGroupBy.end(), field.name )
+                            != aBomJob->m_fieldsGroupBy.end();
+            field.label = ( ( aBomJob->m_fieldsLabels.size() > i )
+                            && !aBomJob->m_fieldsLabels[i].IsEmpty() )
+                                  ? aBomJob->m_fieldsLabels[i]
+                                  : field.name;
+
+            preset.fieldsOrdered.emplace_back( field );
+            i++;
+        }
+
+        preset.sortAsc = aBomJob->m_sortAsc;
+        preset.sortField = aBomJob->m_sortField;
+        preset.filterString = aBomJob->m_filterString;
+        preset.groupSymbols = ( aBomJob->m_fieldsGroupBy.size() > 0 );
+        preset.excludeDNP = aBomJob->m_excludeDNP;
+    }
 
     dataModel.ApplyBomPreset( preset );
 
@@ -391,12 +424,46 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
     }
 
     BOM_FMT_PRESET fmt;
-    fmt.fieldDelimiter = aBomJob->m_fieldDelimiter;
-    fmt.stringDelimiter = aBomJob->m_stringDelimiter;
-    fmt.refDelimiter = aBomJob->m_refDelimiter;
-    fmt.refRangeDelimiter = aBomJob->m_refRangeDelimiter;
-    fmt.keepTabs = aBomJob->m_keepTabs;
-    fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
+
+    // Load a format preset if one is specified
+    if( !aBomJob->m_bomFmtPresetName.IsEmpty() )
+    {
+        // Make sure the built-in presets are loaded
+        for( const BOM_FMT_PRESET& p : BOM_FMT_PRESET::BuiltInPresets() )
+            sch->Settings().m_BomFmtPresets.emplace_back( p );
+
+        // Find the preset
+        BOM_FMT_PRESET* schFmtPreset = nullptr;
+
+        for( BOM_FMT_PRESET& p : sch->Settings().m_BomFmtPresets )
+        {
+            if( p.name == aBomJob->m_bomFmtPresetName )
+            {
+                schFmtPreset = &p;
+                break;
+            }
+        }
+
+        if( !schFmtPreset )
+        {
+            m_reporter->Report( wxString::Format( _( "BOM format preset '%s' not found" ),
+                                                  aBomJob->m_bomFmtPresetName ),
+                                RPT_SEVERITY_ERROR );
+
+            return CLI::EXIT_CODES::ERR_UNKNOWN;
+        }
+
+        fmt = *schFmtPreset;
+    }
+    else
+    {
+        fmt.fieldDelimiter = aBomJob->m_fieldDelimiter;
+        fmt.stringDelimiter = aBomJob->m_stringDelimiter;
+        fmt.refDelimiter = aBomJob->m_refDelimiter;
+        fmt.refRangeDelimiter = aBomJob->m_refRangeDelimiter;
+        fmt.keepTabs = aBomJob->m_keepTabs;
+        fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
+    }
 
     bool res = f.Write( dataModel.Export( fmt ) );
 
diff --git a/include/settings/bom_settings.h b/include/settings/bom_settings.h
index f21ce024c6..85ea4a245b 100644
--- a/include/settings/bom_settings.h
+++ b/include/settings/bom_settings.h
@@ -62,6 +62,8 @@ struct BOM_PRESET
     static BOM_PRESET GroupedByValue();
     static BOM_PRESET GroupedByValueFootprint();
     static BOM_PRESET Attributes();
+
+    static std::vector<BOM_PRESET> BuiltInPresets();
 };
 
 bool operator!=( const BOM_PRESET& lhs, const BOM_PRESET& rhs );
@@ -88,6 +90,8 @@ struct BOM_FMT_PRESET
     static BOM_FMT_PRESET CSV();
     static BOM_FMT_PRESET TSV();
     static BOM_FMT_PRESET Semicolons();
+
+    static std::vector<BOM_FMT_PRESET> BuiltInPresets();
 };
 
 bool operator!=( const BOM_FMT_PRESET& lhs, const BOM_FMT_PRESET& rhs );
diff --git a/kicad/cli/command_sch_export_bom.cpp b/kicad/cli/command_sch_export_bom.cpp
index e20d297682..0623c450de 100644
--- a/kicad/cli/command_sch_export_bom.cpp
+++ b/kicad/cli/command_sch_export_bom.cpp
@@ -35,6 +35,17 @@ CLI::SCH_EXPORT_BOM_COMMAND::SCH_EXPORT_BOM_COMMAND() : COMMAND( "bom" )
     m_argParser.add_description( UTF8STDSTR( _( "Generate a Bill of Material (BOM)" ) ) );
     addCommonArgs( true, true, false, false );
 
+    // Preset options
+    m_argParser.add_argument( ARG_PRESET )
+            .help( UTF8STDSTR( _( ARG_PRESET_DESC ) ) )
+            .default_value( std::string( "" ) )
+            .metavar( "PRESET" );
+
+    m_argParser.add_argument( ARG_FMT_PRESET )
+            .help( UTF8STDSTR( _( ARG_FMT_PRESET_DESC ) ) )
+            .default_value( std::string( "" ) )
+            .metavar( "FMT_PRESET" );
+
     // Field output options
     m_argParser.add_argument( ARG_FIELDS )
             .help( UTF8STDSTR( _( ARG_FIELDS_DESC ) ) )
@@ -127,6 +138,10 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
     bomJob->m_filename = m_argInput;
     bomJob->m_outputFile = m_argOutput;
 
+    bomJob->m_bomPresetName = From_UTF8( m_argParser.get<std::string>( ARG_PRESET ).c_str() );
+    bomJob->m_bomFmtPresetName =
+            From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
+
     // Format options
     bomJob->m_fieldDelimiter =
             From_UTF8( m_argParser.get<std::string>( ARG_FIELD_DELIMITER ).c_str() );
diff --git a/kicad/cli/command_sch_export_bom.h b/kicad/cli/command_sch_export_bom.h
index 906ed30e75..4a6871a274 100644
--- a/kicad/cli/command_sch_export_bom.h
+++ b/kicad/cli/command_sch_export_bom.h
@@ -25,6 +25,12 @@
 
 namespace CLI
 {
+// Options for selecting presets of the export, e.g. GroupedByValue and CSV
+#define ARG_PRESET "--preset"
+#define ARG_PRESET_DESC "Use a named BOM preset settings from the schematic, e.g. \"Grouped By Value\"."
+
+#define ARG_FMT_PRESET "--format-preset"
+#define ARG_FMT_PRESET_DESC "Use a named BOM format preset settings from the schematic, e.g. CSV."
 
 // Options for setting the format of the export, e.g. CSV
 #define ARG_FIELD_DELIMITER "--field-delimiter"