diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini
index 325e3adbd4c2ec9cce69dcd289f420d958416a3d..e7126909d1c93388ad029c3e1fb973cbf76124b6 100644
--- a/config/vufind/Summon.ini
+++ b/config/vufind/Summon.ini
@@ -225,13 +225,20 @@ orFacets = *
 ; below, no facets will be translated.
 translated_facets[] = ContentType
 
-; These facets will be displayed on the Home Page.  If this section is omitted,
+; This section controls the behavior of the Summon/Home screen.
+[HomePage]
+; Content blocks can be selected from the list in searches.ini.
+content[] = FacetList:Summon
+;content[] = Channels:Summon
+
+; These facets will be displayed on the Home Page when FacetList is turned on in
+; the content setting of the [HomePage] section above. If this section is omitted,
 ; the [Advanced_Facets] section will be used instead.
 [HomePage_Facets]
 Language = "Language"
 ContentType = "Format"
 
-; These settings affect the way the [HomePage] facets are displayed
+; These settings affect the way the [HomePage_Facets] are displayed.
 ; NOTE: To make changes take effect immediately, you may need to clear VuFind's
 ; cache after changing this section.
 [HomePage_Facet_Settings]
diff --git a/config/vufind/combined.ini b/config/vufind/combined.ini
index 58249f5ca4518559cb346b9fbb867a4ce353b421..ff466b4b69575ed66b2a15826f3c9a7ca77926e6 100644
--- a/config/vufind/combined.ini
+++ b/config/vufind/combined.ini
@@ -42,6 +42,11 @@
 ; The order of sections in this file will control the display order of search
 ; results on screen.
 
+; This section controls the behavior of the Combined/Home screen.
+[HomePage]
+; Content blocks can be selected from the list in searches.ini.
+content[] = IlsStatusMonitor
+
 ; This section controls how columns will be formatted
 [Layout]
 ; This is the maximum number of columns to use.
diff --git a/config/vufind/facets.ini b/config/vufind/facets.ini
index 958c144e6e9fae5d9a090ca534110cdca8ab44dd..cfa85dd9bacef3447f945487c3454d45adf31e46 100644
--- a/config/vufind/facets.ini
+++ b/config/vufind/facets.ini
@@ -189,15 +189,16 @@ translated_facets[] = callnumber-first:CallNumberFirst
 ;delimited_facets[] = author_id_str
 ;delimited_facets[] = "author_id_str|:::"
 
-; These facets will be displayed on the Home Page.  If this section is omitted,
-; the [Advanced] section will be used instead.
+; These facets will be displayed on the Home Page when FacetList is turned on in
+; the content setting of the [HomePage] section of searches.ini. If this section
+; is omitted, the [Advanced] section will be used instead.
 [HomePage]
 callnumber-first = "Call Number"
 language         = Language
 format           = Format
 ;hierarchy_top_title   = Collections
 
-; These settings affect the way the [HomePage] facets are displayed
+; These settings affect the way the [HomePage] facets are displayed.
 ; NOTE: To make changes take effect immediately, you may need to clear VuFind's
 ; cache after changing this section.
 [HomePage_Settings]
diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini
index f430cb38882d0eba6011b161aecce9e95f5306ab..3dc20f1133a559432909a7cf4ad3e522d3a9d29d 100644
--- a/config/vufind/searches.ini
+++ b/config/vufind/searches.ini
@@ -643,3 +643,20 @@ view=full
 ;params = "qf=title,title_short,callnumber-label,topic,language,author,publishDate mintf=1 mindf=1";
 ; This setting can be used to limit the maximum number of suggestions. Default is 5.
 ;count = 5
+
+; This section controls the behavior of the Search/Home screen.
+[HomePage]
+; Content blocks can be selected from the list below:
+;
+; Channels:[source] - Display the homepage channels for the specified [source].
+; [source] defaults to Solr.
+;
+; FacetList:[source]:[column size] - Display a list of facet values
+; drawn from the [source] backend, with a maximum of [column size] values per
+; column. [source] defaults to Solr and [column size] defaults to 10.
+;
+; IlsStatusMonitor:[target] - Performs an AJAX health check of the ILS and
+; prepends a warning message to the HTML element identified by the jQuery selector
+; provided in [target] (which defaults to .searchHomeContent.
+content[] = IlsStatusMonitor
+content[] = FacetList
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index af6f445fe235eaee193ed26a09f4b5de78e29720..ad7d1ab673bb3bfc5bb487b882d8e0c4ac2bef7a 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -302,6 +302,8 @@ $config = [
             'VuFind\Content\Reviews\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
             'VuFind\Content\Summaries\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
             'VuFind\Content\TOC\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
+            'VuFind\ContentBlock\BlockLoader' => 'VuFind\ContentBlock\BlockLoaderFactory',
+            'VuFind\ContentBlock\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
             'VuFind\Cookie\CookieManager' => 'VuFind\Cookie\CookieManagerFactory',
             'VuFind\Cover\Router' => 'VuFind\Cover\RouterFactory',
             'VuFind\Crypt\HMAC' => 'VuFind\Crypt\HMACFactory',
@@ -475,6 +477,7 @@ $config = [
             'content_reviews' => [ /* see VuFind\Content\Reviews\PluginManager for defaults */ ],
             'content_summaries' => [ /* see VuFind\Content\Summaries\PluginManager for defaults */ ],
             'content_toc' => [ /* see VuFind\Content\TOC\PluginManager for defaults */ ],
+            'contentblock' => [ /* see VuFind\ContentBlock\PluginManager for defaults */ ],
             'db_row' => [ /* see VuFind\Db\Row\PluginManager for defaults */ ],
             'db_table' => [ /* see VuFind\Db\Table\PluginManager for defaults */ ],
             'hierarchy_driver' => [ /* see VuFind\Hierarchy\Driver\PluginManager for defaults */ ],
diff --git a/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php b/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..baa930a78de129c75a11020340716b27e2813e3b
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Abstract base content block.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+/**
+ * Abstract base content block.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class AbstractBase implements ContentBlockInterface
+{
+    /**
+     * Configuration
+     *
+     * @var string
+     */
+    protected $config = '';
+
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings)
+    {
+        $this->config = $settings;
+    }
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext()
+    {
+        // Expose the block object directly by default.
+        return ['block' => $this];
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php b/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..2491d91e3e5b328b9f74954a778cba857af70d41
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Content block loader
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use VuFind\Config\PluginManager as ConfigManager;
+use VuFind\ContentBlock\PluginManager as BlockManager;
+use VuFind\Search\Base\Options;
+use VuFind\Search\Options\PluginManager as OptionsManager;
+use Zend\Config\Config;
+
+/**
+ * Content block plugin manager
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class BlockLoader
+{
+    /**
+     * Options manager.
+     *
+     * @var OptionsManager
+     */
+    protected $optionsManager;
+
+    /**
+     * Config manager.
+     *
+     * @var ConfigManager
+     */
+    protected $configManager;
+
+    /**
+     * Block manager.
+     *
+     * @var BlockManager
+     */
+    protected $blockManager;
+
+    /**
+     * Constructor
+     *
+     * @param OptionsManager $om Options manager
+     * @param ConfigManager  $cm Config manager
+     * @param BlockManager   $bm Block manager
+     */
+    public function __construct(OptionsManager $om, ConfigManager $cm,
+        BlockManager $bm
+    ) {
+        $this->optionsManager = $om;
+        $this->configManager = $cm;
+        $this->blockManager = $bm;
+    }
+
+    /**
+     * Fetch blocks using a search class ID.
+     *
+     * @param string $searchClassId Search class ID
+     *
+     * @return array
+     */
+    public function getFromSearchClassId($searchClassId)
+    {
+        $options = $this->optionsManager->get($searchClassId);
+        return $this->getFromOptions($options);
+    }
+
+    /**
+     * Fetch blocks using an Options object.
+     *
+     * @param Options $options Options object
+     *
+     * @return array
+     */
+    public function getFromOptions(Options $options)
+    {
+        return $this->getFromConfig($options->getSearchIni());
+    }
+
+    /**
+     * Fetch blocks using a configuration name
+     *
+     * @param string $name    Configuration name
+     * @param string $section Section to load from object
+     * @param string $setting Setting to load from section
+     *
+     * @return array
+     */
+    public function getFromConfig($name, $section = 'HomePage',
+        $setting = 'content'
+    ) {
+        $config = $this->configManager->get($name);
+        return $this->getFromConfigObject($config, $section, $setting);
+    }
+
+    /**
+     * Fetch blocks using Config object.
+     *
+     * @param Config $config  Configuration object
+     * @param string $section Section to load from object
+     * @param string $setting Setting to load from section
+     *
+     * @return array
+     */
+    public function getFromConfigObject(Config $config, $section = 'HomePage',
+        $setting = 'content'
+    ) {
+        $blocks = [];
+        if (isset($config->$section->$setting)) {
+            foreach ($config->$section->$setting as $current) {
+                $parts = explode(':', $current, 2);
+                $block = $this->blockManager->get($parts[0]);
+                $block->setConfig($parts[1] ?? null);
+                $blocks[] = $block;
+            }
+        }
+        return $blocks;
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ad84522ef75833254d1a8a9bc713d7201740568
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * BlockLoader factory.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use Interop\Container\ContainerInterface;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+/**
+ * BlockLoader factory.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class BlockLoaderFactory implements FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options sent to factory.');
+        }
+        return new $requestedName(
+            $container->get('VuFind\Search\Options\PluginManager'),
+            $container->get('VuFind\Config\PluginManager'),
+            $container->get('VuFind\ContentBlock\PluginManager')
+        );
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/Channels.php b/module/VuFind/src/VuFind/ContentBlock/Channels.php
new file mode 100644
index 0000000000000000000000000000000000000000..b258db76d3030f5775df2e4436a477537e06cd8b
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/Channels.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Channels content block.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use VuFind\ChannelProvider\ChannelLoader;
+use Zend\Http\PhpEnvironment\Request;
+
+/**
+ * Channels content block.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class Channels implements ContentBlockInterface
+{
+    /**
+     * Request object
+     *
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * Channel loader
+     *
+     * @var ChannelLoader
+     */
+    protected $loader;
+
+    /**
+     * Data source (null to use default found in channels.ini)
+     *
+     * @var string
+     */
+    protected $source = null;
+
+    /**
+     * Constructor
+     *
+     * @param Request       $request Request object
+     * @param ChannelLoader $loader  Channel loader
+     */
+    public function __construct(Request $request, ChannelLoader $loader)
+    {
+        $this->request = $request;
+        $this->loader = $loader;
+    }
+
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings)
+    {
+        if (!empty($settings)) {
+            $this->source = $settings;
+        }
+    }
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext()
+    {
+        $activeChannel = $this->request->getQuery()->get('channelProvider');
+        $token = $this->request->getQuery()->get('channelToken');
+        return $this->loader->getHomeContext($token, $activeChannel, $this->source);
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..39cb267cc2daf1f8c91df892b829ecaf7bceb93c
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Channels factory.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use Interop\Container\ContainerInterface;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+/**
+ * Channels factory.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class ChannelsFactory implements FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options sent to factory.');
+        }
+        return new $requestedName(
+            $container->get('Request'),
+            $container->get('VuFind\ChannelProvider\ChannelLoader')
+        );
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php b/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ed451f42f359a51508477f1534fc0755a2e6ab5
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Content block interface
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+/**
+ * Content block interface
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+interface ContentBlockInterface
+{
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings);
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext();
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetList.php b/module/VuFind/src/VuFind/ContentBlock/FacetList.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8673e001623cb4e467d392d8975f9aee690eddd
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/FacetList.php
@@ -0,0 +1,148 @@
+<?php
+/**
+ * FacetList content block.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use VuFind\Config\PluginManager as ConfigManager;
+use VuFind\Search\FacetCache\PluginManager as FacetCacheManager;
+use Zend\Config\Config;
+
+/**
+ * FacetList content block.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class FacetList implements ContentBlockInterface
+{
+    /**
+     * Number of values to put in each column of results.
+     *
+     * @var int
+     */
+    protected $columnSize = 10;
+
+    /**
+     * Search class ID to use for retrieving facets.
+     *
+     * @var string
+     */
+    protected $searchClassId = 'Solr';
+
+    /**
+     * Configuration manager
+     *
+     * @var ConfigManager
+     */
+    protected $configManager;
+
+    /**
+     * Facet cache plugin manager
+     *
+     * @var FacetCacheManager
+     */
+    protected $facetCacheManager;
+
+    /**
+     * Constructor
+     *
+     * @param FacetCacheManager $fcm Facet cache plugin manager
+     * @param ConfigManager     $cm  Configuration manager
+     */
+    public function __construct(FacetCacheManager $fcm, ConfigManager $cm)
+    {
+        $this->facetCacheManager = $fcm;
+        $this->configManager = $cm;
+    }
+
+    /**
+     * Get an array of hierarchical facets
+     *
+     * @param Config $facetConfig Facet configuration object.
+     *
+     * @return array Facets
+     */
+    protected function getHierarchicalFacets($facetConfig)
+    {
+        return isset($facetConfig->SpecialFacets->hierarchical)
+            ? $facetConfig->SpecialFacets->hierarchical->toArray()
+            : [];
+    }
+
+    /**
+     * Get hierarchical facet sort settings
+     *
+     * @param Config $facetConfig Facet configuration object.
+     *
+     * @return array Array of sort settings keyed by facet
+     */
+    protected function getHierarchicalFacetSortSettings($facetConfig)
+    {
+        return isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions)
+            ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray()
+            : [];
+    }
+
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings)
+    {
+        $parts = explode(':', $settings);
+        $this->searchClassId = empty($parts[0]) ? $this->searchClassId : $parts[0];
+        $this->columnSize = $parts[1] ?? $this->columnSize;
+    }
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext()
+    {
+        $facetCache = $this->facetCacheManager->get($this->searchClassId);
+        $results = $facetCache->getResults();
+        $facetConfig = $this->configManager
+            ->get($results->getOptions()->getFacetsIni());
+        return [
+            'searchClassId' => $this->searchClassId,
+            'columnSize' => $this->columnSize,
+            'facetList' => $facetCache->getList('HomePage'),
+            'hierarchicalFacets' => $this->getHierarchicalFacets($facetConfig),
+            'hierarchicalFacetSortOptions' =>
+                $this->getHierarchicalFacetSortSettings($facetConfig),
+            'results' => $results,
+        ];
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..573fd4dc9f65cba4053ce39d254c24e731cbd02f
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * FacetList content block factory.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use Interop\Container\ContainerInterface;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+/**
+ * FacetList content block factory.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class FacetListFactory implements FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options sent to factory.');
+        }
+        $fcpm = $container->get('VuFind\Search\FacetCache\PluginManager');
+        $cm = $container->get('VuFind\Config\PluginManager');
+        return new $requestedName($fcpm, $cm);
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php b/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php
new file mode 100644
index 0000000000000000000000000000000000000000..02a6b190756c5d0df456f7de862d581363a87103
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * ILS status monitor content block.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+/**
+ * Abstract base content block.
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class IlsStatusMonitor implements ContentBlockInterface
+{
+    /**
+     * Target selector for status message.
+     *
+     * @var string
+     */
+    protected $target = '.searchHomeContent';
+
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings)
+    {
+        $this->target = empty($settings) ? $this->target : $settings;
+    }
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext()
+    {
+        // Expose the block object directly by default.
+        return ['target' => $this->target];
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a99e18eecf1b2bcb637ed2d74a67ac78f4217e0
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Content block plugin manager
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+namespace VuFind\ContentBlock;
+
+/**
+ * Content block plugin manager
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
+ */
+class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
+{
+    /**
+     * Default plugin aliases.
+     *
+     * @var array
+     */
+    protected $aliases = [
+        'channels' => 'VuFind\ContentBlock\Channels',
+        'facetlist' => 'VuFind\ContentBlock\FacetList',
+        'ilsstatusmonitor' => 'VuFind\ContentBlock\IlsStatusMonitor',
+    ];
+
+    /**
+     * Default plugin factories.
+     *
+     * @var array
+     */
+    protected $factories = [
+        'VuFind\ContentBlock\Channels' => 'VuFind\ContentBlock\ChannelsFactory',
+        'VuFind\ContentBlock\FacetList' => 'VuFind\ContentBlock\FacetListFactory',
+        'VuFind\ContentBlock\IlsStatusMonitor' =>
+            'Zend\ServiceManager\Factory\InvokableFactory',
+    ];
+
+    /**
+     * Constructor
+     *
+     * Make sure plugins are properly initialized.
+     *
+     * @param mixed $configOrContainerInstance Configuration or container instance
+     * @param array $v3config                  If $configOrContainerInstance is a
+     * container, this value will be passed to the parent constructor.
+     */
+    public function __construct($configOrContainerInstance = null,
+        array $v3config = []
+    ) {
+        // These objects are not meant to be shared -- every time we retrieve one,
+        // we are building a brand new object.
+        $this->sharedByDefault = false;
+
+        parent::__construct($configOrContainerInstance, $v3config);
+    }
+
+    /**
+     * Return the name of the base class or interface that plug-ins must conform
+     * to.
+     *
+     * @return string
+     */
+    protected function getExpectedInterface()
+    {
+        return 'VuFind\ContentBlock\ContentBlockInterface';
+    }
+}
diff --git a/module/VuFind/src/VuFind/Controller/AbstractSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSearch.php
index 2e03548da99e9276213462e6650c9090e7c08d03..6503ed9e33df3ff347220cb0453629c319899674 100644
--- a/module/VuFind/src/VuFind/Controller/AbstractSearch.php
+++ b/module/VuFind/src/VuFind/Controller/AbstractSearch.php
@@ -249,7 +249,9 @@ class AbstractSearch extends AbstractBase
      */
     public function homeAction()
     {
-        return $this->createViewModel();
+        $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader')
+            ->getFromSearchClassId($this->searchClassId);
+        return $this->createViewModel(compact('blocks'));
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/Controller/CombinedController.php b/module/VuFind/src/VuFind/Controller/CombinedController.php
index 8b9a3ca2732542ed97e552423d1af20f5bbeed31..59522831a4aac87ec6376a7c46c9977a37c2362f 100644
--- a/module/VuFind/src/VuFind/Controller/CombinedController.php
+++ b/module/VuFind/src/VuFind/Controller/CombinedController.php
@@ -53,6 +53,20 @@ class CombinedController extends AbstractSearch
         parent::__construct($sm);
     }
 
+    /**
+     * Home action
+     *
+     * @return mixed
+     */
+    public function homeAction()
+    {
+        // We need to load blocks differently in this controller since it
+        // doesn't follow the usual configuration pattern.
+        $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader')
+            ->getFromConfig('combined');
+        return $this->createViewModel(compact('blocks'));
+    }
+
     /**
      * Single result action (used for AJAX)
      *
@@ -304,6 +318,7 @@ class CombinedController extends AbstractSearch
     protected function getTabConfig($config)
     {
         // Strip out non-tab sections of the configuration:
+        unset($config['HomePage']);
         unset($config['Layout']);
         unset($config['RecommendationModules']);
 
diff --git a/module/VuFind/src/VuFind/Controller/SearchController.php b/module/VuFind/src/VuFind/Controller/SearchController.php
index 8da9abd1427c3704c2c7b4719031fea5753c7e90..2f1934e0d1fd80fbdcc13cb1230f792a7945db85 100644
--- a/module/VuFind/src/VuFind/Controller/SearchController.php
+++ b/module/VuFind/src/VuFind/Controller/SearchController.php
@@ -262,26 +262,6 @@ class SearchController extends AbstractSearch
         return $this->createViewModel($lastSearches);
     }
 
-    /**
-     * Home action
-     *
-     * @return mixed
-     */
-    public function homeAction()
-    {
-        $facetCache = $this->serviceLocator
-            ->get('VuFind\Search\FacetCache\PluginManager')->get('Solr');
-        return $this->createViewModel(
-            [
-                'results' => $facetCache->getResults(),
-                'facetList' => $facetCache->getList('HomePage'),
-                'hierarchicalFacets' => $this->getHierarchicalFacets(),
-                'hierarchicalFacetSortOptions'
-                    => $this->getHierarchicalFacetSortSettings()
-            ]
-        );
-    }
-
     /**
      * New item search form
      *
@@ -597,17 +577,4 @@ class SearchController extends AbstractSearch
             ? $facetConfig->SpecialFacets->hierarchical->toArray()
             : [];
     }
-
-    /**
-     * Get hierarchical facet sort settings
-     *
-     * @return array Array of sort settings keyed by facet
-     */
-    protected function getHierarchicalFacetSortSettings()
-    {
-        $facetConfig = $this->getConfig('facets');
-        return isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions)
-            ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray()
-            : [];
-    }
 }
diff --git a/module/VuFind/src/VuFind/Controller/SummonController.php b/module/VuFind/src/VuFind/Controller/SummonController.php
index c08b94260864e735032f0385931eae91a94b5609..fe12aa78700b54602ae11ece777c9ba10aca38bf 100644
--- a/module/VuFind/src/VuFind/Controller/SummonController.php
+++ b/module/VuFind/src/VuFind/Controller/SummonController.php
@@ -123,23 +123,6 @@ class SummonController extends AbstractSearch
         return $view;
     }
 
-    /**
-     * Home action
-     *
-     * @return mixed
-     */
-    public function homeAction()
-    {
-        $facetCache = $this->serviceLocator
-            ->get('VuFind\Search\FacetCache\PluginManager')->get('Summon');
-        return $this->createViewModel(
-            [
-                'results' => $facetCache->getResults(),
-                'facetList' => $facetCache->getList('HomePage'),
-            ]
-        );
-    }
-
     /**
      * Search action -- call standard results action
      *
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php b/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php
new file mode 100644
index 0000000000000000000000000000000000000000..86a0661aee11930ca4056811ee5d71ec4fb7d96b
--- /dev/null
+++ b/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * ContentBlock view helper
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2018.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\View\Helper\Root;
+
+/**
+ * ContentBlock view helper
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class ContentBlock extends AbstractClassBasedTemplateRenderer
+{
+    /**
+     * Render the output of a ContentBlock plugin.
+     *
+     * @param \VuFind\ContentBlock\ContentBlockInterface $block The ContentBlock
+     * object to render
+     *
+     * @return string
+     */
+    public function __invoke($block)
+    {
+        $template = 'ContentBlock/%s.phtml';
+        $className = get_class($block);
+        $context = $block->getContext();
+        return $this->renderClassTemplate($template, $className, $context);
+    }
+}
diff --git a/themes/bootstrap3/templates/ContentBlock/Channels.phtml b/themes/bootstrap3/templates/ContentBlock/Channels.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..5047df7b340af31b4478809dc0a52bf292f10e37
--- /dev/null
+++ b/themes/bootstrap3/templates/ContentBlock/Channels.phtml
@@ -0,0 +1 @@
+<?=$this->render('channels/channelList.phtml')?>
diff --git a/themes/bootstrap3/templates/ContentBlock/FacetList.phtml b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..10f2933e23c143da42f51e4bef0c4599ef8da521
--- /dev/null
+++ b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml
@@ -0,0 +1,82 @@
+<?php
+  // Load search actions and settings (if any):
+  $options = $this->searchOptions($searchClassId);
+  $basicSearch = $options->getSearchAction();
+  $advSearch = $options->getAdvancedSearchAction();
+  $noJsSupport = $this->config()->nonJavascriptSupportEnabled();
+?>
+<?php if (!empty($facetList)): ?>
+  <div class="search-home-facets">
+    <?php foreach ($facetList as $field => $details): ?>
+      <?php if ($isHierarchy = in_array($field, $hierarchicalFacets ?? [])):
+          $this->headScript()->appendFile('vendor/jsTree/jstree.min.js');
+          $this->headScript()->appendFile('facets.js');
+          $sort = $hierarchicalFacetSortOptions[$field] ?? '';
+          $script = <<<JS
+$(document).ready(function() {
+  $('#facet_{$this->escapeHtml($field)}_container').removeClass('hide');
+  initFacetTree($('#facet_{$this->escapeHtml($field)}'), false);
+});
+JS;
+          echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET');
+        ?>
+        <div id="facet_<?=$this->escapeHtml($field)?>_container" class="home-facet <?=$this->escapeHtmlAttr($field)?> hide">
+          <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
+          <div id="facet_<?=$this->escapeHtml($field)?>" class="jstree-facet"
+              data-facet="<?=$this->escapeHtml($field)?>"
+              data-path="<?=$this->url($basicSearch)?>"
+              data-exclude="0"
+              data-operator="AND"
+              data-exclude-title="<?=$this->transEsc('exclude_facet')?>"
+              data-sort="all">
+          </div>
+        </div>
+        <noscript>
+          <?php if (!$noJsSupport): ?>
+            <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
+            <?=$this->transEsc('Please enable JavaScript.')?>
+          <?php endif; ?>
+      <?php endif; ?>
+      <?php if (!$isHierarchy || $noJsSupport): // do we need regular display? ?>
+        <?php $sortedList = $this->sortFacetList($results, $field, $details['list'], $basicSearch); ?>
+        <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>">
+          <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
+          <div class="home-facet-container">
+            <ul class="home-facet-list">
+              <?php
+                // Special case: two columns for LC call numbers...
+                $maxListLength = $field == 'callnumber-first'
+                  ? $columnSize * 2 : $columnSize;
+
+                // Special case: custom URLs for collections...
+                $moreUrl = $field == 'hierarchy_top_title'
+                  ? $this->url('collections-home') : $this->url($advSearch);
+
+                // Convenience variable:
+                $currentListLength = count($sortedList);
+              ?>
+              <?php $i = 0; foreach ($sortedList as $url => $value):
+                  // Special case: custom URLs for collections...
+                  if ($field == 'hierarchy_top_title') {
+                      $url = $this->url('collections-bytitle') . '?title=' . urlencode($value);
+                  }
+                ?>
+                <li><a href="<?=$url?>"><?=$this->escapeHtml(empty($value) ? '-' : $value)?></a></li>
+                <?php if (++$i >= $currentListLength) break; // end of list? bail out! ?>
+                <?php if ($i >= $maxListLength): // list too long? show more link! ?>
+                  <li><a href="<?=$moreUrl?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li>
+                  <?php break; ?>
+                <?php elseif ($i % $columnSize === 0): // end of column? insert break! ?>
+                  </ul><ul class="home-facet-list">
+                <?php endif; ?>
+              <?php endforeach; ?>
+            </ul>
+          </div>
+        </div>
+      <?php endif; ?>
+      <?php if ($isHierarchy): // close tag opened in matching if above ?>
+        </noscript>
+      <?php endif; ?>
+    <?php endforeach; ?>
+  </div>
+<?php endif; ?>
diff --git a/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml b/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..43448ad2d149bae44f05074e90658b7fd2934c57
--- /dev/null
+++ b/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml
@@ -0,0 +1,16 @@
+<?php
+$ilsStatusScript = <<<JS
+$(document).ready(function() {
+  $.ajax({
+      dataType: 'json',
+      method: 'GET',
+      data: {'offlineModeMsg':'ils_offline_home_message'},
+      url: VuFind.path + '/AJAX/JSON?method=getIlsStatus',
+      success: function(response) {
+          $('{$target}').prepend(response.data);
+      }
+  });
+});
+JS;
+echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $ilsStatusScript, 'SET');
+?>
diff --git a/themes/bootstrap3/templates/search/home.phtml b/themes/bootstrap3/templates/search/home.phtml
index c9fde55bb56d1a7bbfd822feb64930d7480509f2..f9c8cff4cbbf84656d1cfb3b3f379416af2276a1 100644
--- a/themes/bootstrap3/templates/search/home.phtml
+++ b/themes/bootstrap3/templates/search/home.phtml
@@ -10,110 +10,12 @@
     $this->searchClassId = 'Solr';
   }
 
-  // Load search actions and settings (if any):
-  $options = $this->searchOptions($this->searchClassId);
-  $basicSearch = $options->getSearchAction();
-  $advSearch = $options->getAdvancedSearchAction();
-  $columnSize = 10;
-  $noJsSupport = $this->config()->nonJavascriptSupportEnabled();
-
   $this->layout()->breadcrumbs = false;
 ?>
 
 <div class="searchHomeContent">
-  <?php
-  $ilsStatusScript = <<<JS
-$(document).ready(function() {
-  $.ajax({
-      dataType: 'json',
-      method: 'GET',
-      data: {'offlineModeMsg':'ils_offline_home_message'},
-      url: VuFind.path + '/AJAX/JSON?method=getIlsStatus',
-      success: function(response) {
-          $('.searchHomeContent').prepend(response.data);
-      }
-  });
-});
-JS;
-  ?>
-  <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $ilsStatusScript, 'SET'); ?>
-
   <?=$this->context($this)->renderInContext("search/searchbox.phtml", ['ignoreHiddenFilterMemory' => true])?>
   <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$("#searchForm_lookfor").focus();', 'SET'); ?>
 </div>
 
-<?php if (!empty($facetList)): ?>
-  <div class="search-home-facets">
-    <?php foreach ($facetList as $field => $details): ?>
-      <?php if ($isHierarchy = in_array($field, $this->hierarchicalFacets ?? [])):
-          $this->headScript()->appendFile('vendor/jsTree/jstree.min.js');
-          $this->headScript()->appendFile('facets.js');
-          $sort = $this->hierarchicalFacetSortOptions[$field] ?? '';
-          $script = <<<JS
-$(document).ready(function() {
-  $('#facet_{$this->escapeHtml($field)}_container').removeClass('hide');
-  initFacetTree($('#facet_{$this->escapeHtml($field)}'), false);
-});
-JS;
-          echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET');
-        ?>
-        <div id="facet_<?=$this->escapeHtml($field)?>_container" class="home-facet <?=$this->escapeHtmlAttr($field)?> hide">
-          <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
-          <div id="facet_<?=$this->escapeHtml($field)?>" class="jstree-facet"
-              data-facet="<?=$this->escapeHtml($field)?>"
-              data-path="<?=$this->url($basicSearch)?>"
-              data-exclude="0"
-              data-operator="AND"
-              data-exclude-title="<?=$this->transEsc('exclude_facet')?>"
-              data-sort="all">
-          </div>
-        </div>
-        <noscript>
-          <?php if (!$noJsSupport): ?>
-            <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
-            <?=$this->transEsc('Please enable JavaScript.')?>
-          <?php endif; ?>
-      <?php endif; ?>
-      <?php if (!$isHierarchy || $noJsSupport): // do we need regular display? ?>
-        <?php $sortedList = $this->sortFacetList($this->results, $field, $details['list'], $basicSearch); ?>
-        <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>">
-          <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2>
-          <div class="home-facet-container">
-            <ul class="home-facet-list">
-              <?php
-                // Special case: two columns for LC call numbers...
-                $maxListLength = $field == 'callnumber-first'
-                  ? $columnSize * 2 : $columnSize;
-
-                // Special case: custom URLs for collections...
-                $moreUrl = $field == 'hierarchy_top_title'
-                  ? $this->url('collections-home') : $this->url($advSearch);
-
-                // Convenience variable:
-                $currentListLength = count($sortedList);
-              ?>
-              <?php $i = 0; foreach ($sortedList as $url => $value):
-                  // Special case: custom URLs for collections...
-                  if ($field == 'hierarchy_top_title') {
-                      $url = $this->url('collections-bytitle') . '?title=' . urlencode($value);
-                  }
-                ?>
-                <li><a href="<?=$url?>"><?=$this->escapeHtml(empty($value) ? '-' : $value)?></a></li>
-                <?php if (++$i >= $currentListLength) break; // end of list? bail out! ?>
-                <?php if ($i >= $maxListLength): // list too long? show more link! ?>
-                  <li><a href="<?=$moreUrl?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li>
-                  <?php break; ?>
-                <?php elseif ($i % $columnSize === 0): // end of column? insert break! ?>
-                  </ul><ul class="home-facet-list">
-                <?php endif; ?>
-              <?php endforeach; ?>
-            </ul>
-          </div>
-        </div>
-      <?php endif; ?>
-      <?php if ($isHierarchy): // close tag opened in matching if above ?>
-        </noscript>
-      <?php endif; ?>
-    <?php endforeach; ?>
-  </div>
-<?php endif; ?>
+<?=implode('', array_map([$this, 'contentBlock'], $blocks ?? []))?>
\ No newline at end of file
diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php
index d6e8154204ce1c5a908f29ad51008020c93fe91c..d083f57ebf96b27d939c5e35666fdcedbf3c5e36 100644
--- a/themes/root/theme.config.php
+++ b/themes/root/theme.config.php
@@ -13,6 +13,7 @@ return [
             'VuFind\View\Helper\Root\Cart' => 'VuFind\View\Helper\Root\Factory::getCart',
             'VuFind\View\Helper\Root\Citation' => 'VuFind\View\Helper\Root\Factory::getCitation',
             'VuFind\View\Helper\Root\Config' => 'VuFind\View\Helper\Root\ConfigFactory',
+            'VuFind\View\Helper\Root\ContentBlock' => 'Zend\ServiceManager\Factory\InvokableFactory',
             'VuFind\View\Helper\Root\Context' => 'Zend\ServiceManager\Factory\InvokableFactory',
             'VuFind\View\Helper\Root\CurrentPath' => 'Zend\ServiceManager\Factory\InvokableFactory',
             'VuFind\View\Helper\Root\DateTime' => 'VuFind\View\Helper\Root\Factory::getDateTime',
@@ -70,6 +71,7 @@ return [
             'cart' => 'VuFind\View\Helper\Root\Cart',
             'citation' => 'VuFind\View\Helper\Root\Citation',
             'config' => 'VuFind\View\Helper\Root\Config',
+            'contentBlock' => 'VuFind\View\Helper\Root\ContentBlock',
             'context' => 'VuFind\View\Helper\Root\Context',
             'currentPath' => 'VuFind\View\Helper\Root\CurrentPath',
             'dateTime' => 'VuFind\View\Helper\Root\DateTime',