diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
index c525d9d24..6194fc7d4 100644
--- a/mesh/cfgmod-server.c
+++ b/mesh/cfgmod-server.c
@@ -34,6 +34,12 @@
 
 #define CFG_MAX_MSG_LEN 380
 
+/* Supported composition pages, sorted high to low */
+/* Only page 0 is currently supported */
+static const uint8_t supported_pages[] = {
+	0
+};
+
 static void send_pub_status(struct mesh_node *node, uint16_t net_idx,
 			uint16_t src, uint16_t dst,
 			uint8_t status, uint16_t ele_addr, uint32_t mod_id,
@@ -701,6 +707,33 @@ static void node_reset(void *user_data)
 	node_remove(node);
 }
 
+static uint16_t get_composition(struct mesh_node *node, uint8_t page,
+								uint8_t *buf)
+{
+	const uint8_t *comp;
+	uint16_t len = 0;
+	size_t i;
+
+	for (i = 0; i < sizeof(supported_pages); i++) {
+		if (page < supported_pages[i])
+			continue;
+
+		page = supported_pages[i];
+		comp = node_get_comp(node, page, &len);
+
+		if (!page || len)
+			break;
+	}
+
+	if (!len)
+		return 0;
+
+	*buf++ = page;
+	memcpy(buf, comp, len);
+
+	return len + 1;
+}
+
 static bool cfg_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx,
 				uint16_t net_idx, const uint8_t *data,
 				uint16_t size, const void *user_data)
@@ -746,16 +779,9 @@ static bool cfg_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx,
 		if (size != 1)
 			return false;
 
-		/* Only page 0 is currently supported */
-		if (pkt[0] != 0) {
-			l_debug("Unsupported page number %d", pkt[0]);
-			l_debug("Returning page number 0");
-		}
 		long_msg = l_malloc(CFG_MAX_MSG_LEN);
 		n = mesh_model_opcode_set(OP_DEV_COMP_STATUS, long_msg);
-		long_msg[n++] = 0;
-		n += node_generate_comp(node, long_msg + n,
-							CFG_MAX_MSG_LEN - n);
+		n += get_composition(node, pkt[0], long_msg + n);
 
 		break;
 
diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 661775f95..88f715fc1 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -430,6 +430,54 @@ static bool read_device_key(json_object *jobj, uint8_t key_buf[16])
 	return true;
 }
 
+static bool read_comp_pages(json_object *jobj, struct mesh_config_node *node)
+{
+	json_object *jarray, *jentry;
+	struct mesh_config_comp_page *page;
+	int len;
+	int i;
+
+	if (!json_object_object_get_ex(jobj, "pages", &jarray))
+		return true;
+
+	if (json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	len = json_object_array_length(jarray);
+
+	for (i = 0; i < len; i++) {
+		size_t clen;
+		char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str = (char *)json_object_get_string(jentry);
+		clen = strlen(str);
+
+		if (clen < ((MIN_COMP_SIZE * 2) + 1))
+			continue;
+
+		clen = (clen / 2) - 1;
+
+		page = l_malloc(sizeof(struct mesh_config_comp_page) + clen);
+
+		if (!str2hex(str + 2, clen * 2, page->data, clen))
+			goto parse_fail;
+
+		if (sscanf(str, "%02hhx", &page->page_num) != 1)
+			goto parse_fail;
+
+		page->len = clen;
+
+		l_queue_push_tail(node->pages, page);
+	}
+
+	return true;
+
+parse_fail:
+	l_free(page);
+	return false;
+}
+
 static bool read_app_keys(json_object *jobj, struct mesh_config_node *node)
 {
 	json_object *jarray;
@@ -1384,6 +1432,11 @@ static bool read_node(json_object *jnode, struct mesh_config_node *node)
 		return false;
 	}
 
+	if (!read_comp_pages(jnode, node)) {
+		l_info("Failed to read Composition Pages");
+		return false;
+	}
+
 	if (!parse_elements(jvalue, node)) {
 		l_info("Failed to parse elements");
 		return false;
@@ -1889,6 +1942,113 @@ bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr,
 	return save_config(cfg->jnode, cfg->node_dir_path);
 }
 
+static void del_page(json_object *jarray, uint8_t page)
+{
+	char buf[3];
+	int i, len;
+
+	if (!jarray)
+		return;
+
+	snprintf(buf, 3, "%2.2x", page);
+
+	len = json_object_array_length(jarray);
+
+	for (i = 0; i < len; i++) {
+		json_object *jentry;
+		char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str = (char *)json_object_get_string(jentry);
+
+		/* Delete matching page(s) */
+		if (!memcmp(str, buf, 2))
+			json_object_array_del_idx(jarray, i, 1);
+	}
+}
+
+bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
+						uint8_t *data, uint16_t size)
+{
+	json_object *jnode, *jstring, *jarray = NULL;
+	char *buf;
+	int len;
+
+	if (!cfg)
+		return false;
+
+	jnode = cfg->jnode;
+
+	json_object_object_get_ex(jnode, "pages", &jarray);
+
+	len = (size * 2) + 3;
+	buf = l_malloc(len);
+	snprintf(buf, len, "%2.2x", page);
+	hex2str(data, size, buf + 2, len - 2);
+
+	if (jarray && jarray_has_string(jarray, buf, len)) {
+		l_free(buf);
+		return true;
+	} else if (!jarray) {
+		jarray = json_object_new_array();
+		json_object_object_add(jnode, "pages", jarray);
+	} else
+		del_page(jarray, page);
+
+	jstring = json_object_new_string(buf);
+	json_object_array_add(jarray, jstring);
+	l_free(buf);
+
+	return save_config(jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw)
+{
+	json_object *jnode, *jarray = NULL;
+	uint8_t *data;
+	char *str;
+	char old_buf[3];
+	int i, len, dlen = 0;
+	bool status = true;
+
+	if (!cfg || old == nw)
+		return false;
+
+	jnode = cfg->jnode;
+
+	json_object_object_get_ex(jnode, "pages", &jarray);
+
+	if (!jarray)
+		return false;
+
+	snprintf(old_buf, 3, "%2.2x", old);
+	data = l_malloc(MAX_MSG_LEN);
+
+	len = json_object_array_length(jarray);
+
+	for (i = 0; i < len; i++) {
+		json_object *jentry;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str = (char *)json_object_get_string(jentry);
+
+		/* Delete matching page(s) but save data*/
+		if (!memcmp(str, old_buf, 2)) {
+			dlen = strlen(str + 2);
+			str2hex(str + 2, dlen, data, MAX_MSG_LEN);
+			dlen /= 2;
+			json_object_array_del_idx(jarray, i, 1);
+		}
+	}
+
+	if (dlen)
+		status = mesh_config_comp_page_add(cfg, nw, data, dlen);
+
+	l_free(data);
+
+	return status;
+}
+
 bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
 						uint32_t mod_id, bool vendor,
 						struct mesh_config_sub *sub)
@@ -2212,6 +2372,7 @@ static bool load_node(const char *fname, const uint8_t uuid[16],
 	node.elements = l_queue_new();
 	node.netkeys = l_queue_new();
 	node.appkeys = l_queue_new();
+	node.pages = l_queue_new();
 
 	result = read_node(jnode, &node);
 
@@ -2238,6 +2399,7 @@ static bool load_node(const char *fname, const uint8_t uuid[16],
 	l_free(node.net_transmit);
 	l_queue_destroy(node.netkeys, l_free);
 	l_queue_destroy(node.appkeys, l_free);
+	l_queue_destroy(node.pages, l_free);
 	l_queue_destroy(node.elements, free_element);
 
 	if (!result)
diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h
index 9f30e693b..7dfa9f20c 100644
--- a/mesh/mesh-config.h
+++ b/mesh/mesh-config.h
@@ -17,6 +17,8 @@
  *
  */
 
+#define MIN_COMP_SIZE 14
+
 struct mesh_config;
 
 struct mesh_config_sub {
@@ -88,10 +90,17 @@ struct mesh_config_transmit {
 	uint8_t count;
 };
 
+struct mesh_config_comp_page {
+	uint16_t len;
+	uint8_t page_num;
+	uint8_t data[];
+};
+
 struct mesh_config_node {
 	struct l_queue *elements;
 	struct l_queue *netkeys;
 	struct l_queue *appkeys;
+	struct l_queue *pages;
 	uint32_t seq_number;
 	uint32_t iv_index;
 	bool iv_update;
@@ -139,6 +148,9 @@ bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode,
 bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl);
 bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword,
 								int value);
+bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
+						uint8_t *data, uint16_t size);
+bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw);
 bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
 						bool vendor, uint32_t mod_id,
 							uint16_t app_idx);
diff --git a/mesh/node.c b/mesh/node.c
index 3e888ce61..c61167bda 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -46,8 +46,6 @@
 #include "mesh/manager.h"
 #include "mesh/node.h"
 
-#define MIN_COMP_SIZE 14
-
 #define MESH_NODE_PATH_PREFIX "/node"
 
 /* Default values for a new locally created node */
@@ -81,6 +79,7 @@ struct node_composition {
 struct mesh_node {
 	struct mesh_net *net;
 	struct l_queue *elements;
+	struct l_queue *pages;
 	char *app_path;
 	char *owner;
 	char *obj_path;
@@ -266,6 +265,7 @@ static struct mesh_node *node_new(const uint8_t uuid[16])
 	node = l_new(struct mesh_node, 1);
 	node->net = mesh_net_new(node);
 	node->elements = l_queue_new();
+	node->pages = l_queue_new();
 	memcpy(node->uuid, uuid, sizeof(node->uuid));
 	set_defaults(node);
 
@@ -335,6 +335,7 @@ static void free_node_resources(void *data)
 	/* Free dynamic resources */
 	free_node_dbus_resources(node);
 	l_queue_destroy(node->elements, element_free);
+	l_queue_destroy(node->pages, l_free);
 	mesh_agent_remove(node->agent);
 	mesh_config_release(node->cfg);
 	mesh_net_free(node->net);
@@ -557,8 +558,15 @@ static bool init_from_storage(struct mesh_config_node *db_node,
 
 	l_queue_foreach(db_node->netkeys, set_net_key, node);
 
-	if (db_node->appkeys)
-		l_queue_foreach(db_node->appkeys, set_appkey, node);
+	l_queue_foreach(db_node->appkeys, set_appkey, node);
+
+	while (l_queue_length(db_node->pages)) {
+		struct mesh_config_comp_page *page;
+
+		/* Move the composition pages to the node struct */
+		page = l_queue_pop_head(db_node->pages);
+		l_queue_push_tail(node->pages, page);
+	}
 
 	mesh_net_set_seq_num(node->net, node->seq_number);
 	mesh_net_set_default_ttl(node->net, node->ttl);
@@ -877,7 +885,8 @@ uint8_t node_friend_mode_get(struct mesh_node *node)
 	return node->friend;
 }
 
-uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
+static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
+								uint16_t sz)
 {
 	uint16_t n, features;
 	uint16_t num_ele = 0;
@@ -991,6 +1000,78 @@ element_done:
 	return n;
 }
 
+static bool match_page(const void *a, const void *b)
+{
+	const struct mesh_config_comp_page *page = a;
+	uint8_t page_num = L_PTR_TO_UINT(b);
+
+	return page->page_num == page_num;
+}
+
+bool node_set_comp(struct mesh_node *node, uint8_t page_num,
+					const uint8_t *data, uint16_t len)
+{
+	struct mesh_config_comp_page *page;
+
+	if (!node || len < MIN_COMP_SIZE)
+		return false;
+
+	page = l_queue_remove_if(node->pages, match_page,
+						L_UINT_TO_PTR(page_num));
+
+	l_free(page);
+
+	page = l_malloc(sizeof(struct mesh_config_comp_page) + len);
+	page->len = len;
+	page->page_num = page_num;
+	memcpy(page->data, data, len);
+	l_queue_push_tail(node->pages, page);
+
+	mesh_config_comp_page_add(node->cfg, page_num, page->data, len);
+
+	return true;
+}
+
+const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
+								uint16_t *len)
+{
+	struct mesh_config_comp_page *page = NULL;
+
+	if (node)
+		page = l_queue_find(node->pages, match_page,
+						L_UINT_TO_PTR(page_num));
+
+	if (!page) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = page->len;
+	return page->data;
+}
+
+bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with)
+{
+	struct mesh_config_comp_page *old_page, *keep;
+
+	if (!node)
+		return false;
+
+	keep = l_queue_find(node->pages, match_page, L_UINT_TO_PTR(with));
+
+	if (!keep)
+		return false;
+
+	old_page = l_queue_remove_if(node->pages, match_page,
+							L_UINT_TO_PTR(retire));
+
+	l_free(old_page);
+	keep->page_num = retire;
+	mesh_config_comp_page_mv(node->cfg, with, retire);
+
+	return true;
+}
+
 static void attach_io(void *a, void *b)
 {
 	struct mesh_node *node = a;
@@ -1486,27 +1567,30 @@ static void update_model_options(struct mesh_node *node,
 
 static bool check_req_node(struct managed_obj_request *req)
 {
-	uint8_t node_comp[MAX_MSG_LEN - 2];
-	uint8_t attach_comp[MAX_MSG_LEN - 2];
-	uint16_t offset = 10;
-	uint16_t node_len = node_generate_comp(req->node, node_comp,
-							sizeof(node_comp));
+	struct mesh_node *node;
+	const int offset = 8;
+	uint16_t node_len, len;
+	uint8_t comp[MAX_MSG_LEN - 2];
+	const uint8_t *node_comp;
 
-	if (!node_len)
-		return false;
+	if (req->type == REQUEST_TYPE_ATTACH)
+		node = req->attach;
+	else
+		node = req->node;
 
-	if (req->type == REQUEST_TYPE_ATTACH) {
-		uint16_t attach_len = node_generate_comp(req->attach,
-					attach_comp, sizeof(attach_comp));
+	node_comp = node_get_comp(node, 0, &node_len);
+	len = node_generate_comp(node, comp, sizeof(comp));
 
-		/* Verify only element/models composition */
-		if (node_len != attach_len ||
-				memcmp(&node_comp[offset], &attach_comp[offset],
-							node_len - offset)) {
-			l_debug("Failed to verify app's composition data");
-			return false;
-		}
-	}
+	/* If no page 0 exists, save it and return */
+	if (req->type != REQUEST_TYPE_ATTACH || !node_len || !node_comp)
+		return node_set_comp(node, 0, comp, len);
+
+	if (node_len != len || memcmp(&node_comp[offset], &comp[offset],
+							node_len - offset))
+		return false;
+
+	else if (memcmp(node_comp, comp, node_len))
+		return node_set_comp(node, 0, comp, len);
 
 	return true;
 }
diff --git a/mesh/node.h b/mesh/node.h
index 6c4542a78..df058458a 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -63,7 +63,11 @@ struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx,
 uint16_t node_get_crpl(struct mesh_node *node);
 bool node_init_from_storage(struct mesh_node *node, const uint8_t uuid[16],
 					struct mesh_config_node *db_node);
-uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz);
+const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
+								uint16_t *len);
+bool node_set_comp(struct mesh_node *node, uint8_t page_num,
+					const uint8_t *data, uint16_t len);
+bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with);
 uint8_t node_lpn_mode_get(struct mesh_node *node);
 bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
 							uint16_t interval);
