diff --git a/themes/icarus/util/cache.js b/themes/icarus/util/cache.js
new file mode 100644
index 0000000..75055e6
--- /dev/null
+++ b/themes/icarus/util/cache.js
@@ -0,0 +1,48 @@
+/**
+ * View cache utility functions.
+ * @module util/cache
+ */
+const crypto = require('crypto');
+const { Component } = require('inferno'); // eslint-disable-line no-unused-vars
+const { createElement } = require('inferno-create-element');
+
+const cache = {};
+
+function computeHash(props) {
+ return crypto.createHash('md5').update(JSON.stringify(props)).digest('hex');
+}
+
+module.exports = {
+ /**
+ * Create cached component from a given component class.
+ * The cache ID is caculated from the input props.
+ *
+ * @param {Component} type JSX component class
+ * @param {string} prefix Cache ID prefix
+ * @param {Function} transform Transform the input props to target props and
+ * its result is used to compute cache ID
+ * @returns The new cachable functional component, which uses the transform
function and the
+ * component props to calculate the cache ID.
+ *
+ * The cache ID will be used to determine whether the same element with the exact same
+ * props has been created and cached. If so, the cached element will be returned.
+ * If the cache ID can be computed, a new element will be created use the
+ * createElement
function of the inferno.js, then it will be cached and returned.
+ * If the transform
ed props is empty (!transform(props)
), the cachable
+ * functional component will also return null element when createElement
is called
+ * on it.
+ */
+ cacheComponent(type, prefix, transform) {
+ return (props) => {
+ const targetProps = transform(props);
+ if (targetProps === null || typeof targetProps !== 'object') {
+ return null;
+ }
+ const cacheId = prefix + '-' + computeHash(targetProps);
+ if (!cache[cacheId]) {
+ cache[cacheId] = createElement(type, targetProps);
+ }
+ return cache[cacheId];
+ };
+ },
+};