diff --git a/lib/wp/meson.build b/lib/wp/meson.build
index 69885bd8b41f1d8a94c28b49565700f485b54e69..d4f6371c87dcee4e126118f3f1e118cd672adaa6 100644
--- a/lib/wp/meson.build
+++ b/lib/wp/meson.build
@@ -5,6 +5,7 @@ wp_lib_sources = [
   'factory.c',
   'module.c',
   'policy.c',
+  'properties.c',
   'proxy.c',
   'proxy-node.c',
   'proxy-port.c',
@@ -20,6 +21,7 @@ wp_lib_headers = [
   'factory.h',
   'module.h',
   'policy.h',
+  'properties.h',
   'proxy.h',
   'proxy-node.h',
   'proxy-port.h',
diff --git a/lib/wp/properties.c b/lib/wp/properties.c
new file mode 100644
index 0000000000000000000000000000000000000000..aed330685e39e92b543ce128078df7f36f1965bf
--- /dev/null
+++ b/lib/wp/properties.c
@@ -0,0 +1,222 @@
+/* WirePlumber
+ *
+ * Copyright © 2019 Collabora Ltd.
+ *    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "properties.h"
+
+#include <errno.h>
+#include <pipewire/properties.h>
+
+enum {
+  FLAG_IS_DICT = (1<<1),
+  FLAG_NO_OWNERSHIP = (1<<2),
+};
+
+struct _WpProperties
+{
+  guint32 flags;
+  union {
+    struct pw_properties *props;
+    const struct spa_dict *dict;
+  };
+};
+
+G_DEFINE_BOXED_TYPE(WpProperties, wp_properties, wp_properties_ref, wp_properties_unref)
+
+WpProperties *
+wp_properties_new_empty (void)
+{
+  WpProperties * self = g_rc_box_new (WpProperties);
+  self->flags = 0;
+  self->props = pw_properties_new (NULL, NULL);
+  return self;
+}
+
+WpProperties *
+wp_properties_new (const gchar * key, ...)
+{
+  WpProperties * self;
+  va_list varargs;
+
+  va_start(varargs, key);
+  self = wp_properties_new_valist (key, varargs);
+  va_end(varargs);
+
+  return self;
+}
+
+WpProperties *
+wp_properties_new_valist (const gchar * key, va_list varargs)
+{
+  WpProperties * self = wp_properties_new_empty ();
+  const gchar *value;
+
+  while (key != NULL) {
+    value = va_arg(varargs, gchar *);
+    if (value && key[0])
+      wp_properties_set (self, key, value);
+    key = va_arg(varargs, gchar *);
+  }
+
+  return self;
+}
+
+WpProperties *
+wp_properties_new_string (const gchar * str)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = 0;
+  self->props = pw_properties_new_string (str);
+  return self;
+}
+
+WpProperties *
+wp_properties_new_wrap (struct pw_properties * props)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (props != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = FLAG_NO_OWNERSHIP;
+  self->props = props;
+  return self;
+}
+
+WpProperties *
+wp_properties_new_take (struct pw_properties * props)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (props != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = 0;
+  self->props = props;
+  return self;
+}
+
+WpProperties *
+wp_properties_new_copy (const struct pw_properties * props)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (props != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = 0;
+  self->props = pw_properties_copy (props);
+  return self;
+}
+
+WpProperties *
+wp_properties_new_wrap_dict (const struct spa_dict * dict)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (dict != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = FLAG_NO_OWNERSHIP | FLAG_IS_DICT;
+  self->dict = dict;
+  return self;
+}
+
+WpProperties *
+wp_properties_new_copy_dict (const struct spa_dict * dict)
+{
+  WpProperties * self;
+
+  g_return_val_if_fail (dict != NULL, NULL);
+
+  self = g_rc_box_new (WpProperties);
+  self->flags = 0;
+  self->props = pw_properties_new_dict (dict);
+  return self;
+}
+
+static void
+wp_properties_free (WpProperties * self)
+{
+  if (!(self->flags & FLAG_NO_OWNERSHIP))
+    pw_properties_free (self->props);
+}
+
+WpProperties *
+wp_properties_ref (WpProperties * self)
+{
+  return g_rc_box_acquire (self);
+}
+
+void
+wp_properties_unref (WpProperties * self)
+{
+  g_rc_box_release_full (self, (GDestroyNotify) wp_properties_free);
+}
+
+const gchar *
+wp_properties_get (WpProperties * self, const gchar * key)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  return spa_dict_lookup (wp_properties_peek_dict (self), key);
+}
+
+gint
+wp_properties_set (WpProperties * self, const gchar * key,
+    const gchar * value)
+{
+  g_return_val_if_fail (self != NULL, -EINVAL);
+  g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
+
+  return pw_properties_set (self->props, key, value);
+}
+
+gint
+wp_properties_setf (WpProperties * self, const gchar * key,
+    const gchar * format, ...)
+{
+  gint res;
+  va_list varargs;
+
+  va_start (varargs, format);
+  res = wp_properties_setf_valist (self, key, format, varargs);
+  va_end (varargs);
+
+  return res;
+}
+
+gint
+wp_properties_setf_valist (WpProperties * self, const gchar * key,
+    const gchar * format, va_list args)
+{
+  g_return_val_if_fail (self != NULL, -EINVAL);
+  g_return_val_if_fail (!(self->flags & FLAG_IS_DICT), -EINVAL);
+
+  return pw_properties_setva (self->props, key, format, args);
+}
+
+const struct spa_dict *
+wp_properties_peek_dict (WpProperties * self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return (self->flags & FLAG_IS_DICT) ? self->dict : &self->props->dict;
+}
+
+struct pw_properties *
+wp_properties_to_pw_properties (WpProperties * self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return pw_properties_new_dict (wp_properties_peek_dict (self));
+}
diff --git a/lib/wp/properties.h b/lib/wp/properties.h
new file mode 100644
index 0000000000000000000000000000000000000000..c295ed7d4640572859f13ece4790d0cea69d0f70
--- /dev/null
+++ b/lib/wp/properties.h
@@ -0,0 +1,55 @@
+/* WirePlumber
+ *
+ * Copyright © 2019 Collabora Ltd.
+ *    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef __WIREPLUMBER_PROPERTIES_H__
+#define __WIREPLUMBER_PROPERTIES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct pw_properties;
+struct spa_dict;
+
+#define WP_TYPE_PROPERTIES (wp_properties_get_type ())
+GType wp_properties_get_type (void);
+
+typedef struct _WpProperties WpProperties;
+
+WpProperties * wp_properties_new_empty (void);
+WpProperties * wp_properties_new (const gchar * key, ...) G_GNUC_NULL_TERMINATED;
+WpProperties * wp_properties_new_valist (const gchar * key, va_list args);
+WpProperties * wp_properties_new_string (const gchar * str);
+
+WpProperties * wp_properties_new_wrap (struct pw_properties * props);
+WpProperties * wp_properties_new_take (struct pw_properties * props);
+WpProperties * wp_properties_new_copy (const struct pw_properties * props);
+
+WpProperties * wp_properties_new_wrap_dict (const struct spa_dict * dict);
+WpProperties * wp_properties_new_copy_dict (const struct spa_dict * dict);
+
+WpProperties * wp_properties_ref (WpProperties * self);
+void wp_properties_unref (WpProperties * self);
+
+const gchar * wp_properties_get (WpProperties * self, const gchar * key);
+
+gint wp_properties_set (WpProperties * self, const gchar * key,
+    const gchar * value);
+gint wp_properties_setf (WpProperties * self, const gchar * key,
+    const gchar * format, ...) G_GNUC_PRINTF(3, 4);
+gint wp_properties_setf_valist (WpProperties * self, const gchar * key,
+    const gchar * format, va_list args);
+
+const struct spa_dict * wp_properties_peek_dict (WpProperties * self);
+struct pw_properties * wp_properties_to_pw_properties (WpProperties * self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpProperties, wp_properties_unref)
+
+G_END_DECLS
+
+#endif
diff --git a/lib/wp/wp.h b/lib/wp/wp.h
index 4f277323e15d3e6cc327d71c304343c92d5dd17f..f052cfe722025c4eb3aaaf06934d37f058279483 100644
--- a/lib/wp/wp.h
+++ b/lib/wp/wp.h
@@ -12,6 +12,7 @@
 #include "factory.h"
 #include "module.h"
 #include "policy.h"
+#include "properties.h"
 #include "proxy.h"
 #include "proxy-link.h"
 #include "proxy-node.h"