diff --git a/src/app.c b/src/app.c
new file mode 100644
index 0000000000000000000000000000000000000000..12e52dc86441ddb2b0445f627dc82df3538b6442
--- /dev/null
+++ b/src/app.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * SPDX-License-Identifier: GPL-3.0+
+ * Author: Guido Günther <agx@sigxcpu.org>
+ */
+
+#define G_LOG_DOMAIN "phosh-app"
+
+#include "config.h"
+#include "app.h"
+#include "phosh.h"
+
+#include <gio/gdesktopappinfo.h>
+
+/**
+ * SECTION:phosh-app
+ * @short_description: An app in the faovorites overview
+ * @Title: PhoshApp
+ *
+ * The #PhoshApp is used to select a running application
+ * in the favorites overview.
+ */
+
+#define APP_ICON_SIZE GTK_ICON_SIZE_DIALOG
+
+enum {
+  PHOSH_APP_PROP_0,
+  PHOSH_APP_PROP_APP_ID,
+  PHOSH_APP_PROP_TITLE,
+  PHOSH_APP_PROP_LAST_PROP,
+};
+static GParamSpec *props[PHOSH_APP_PROP_LAST_PROP];
+
+typedef struct
+{
+  GtkWidget *image;
+  GtkWidget *box;
+
+  char *app_id;
+  char *title;
+  GDesktopAppInfo *info;
+} PhoshAppPrivate;
+
+
+struct _PhoshApp
+{
+  GtkButton parent;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(PhoshApp, phosh_app, GTK_TYPE_BUTTON)
+
+
+static void
+phosh_app_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+  PhoshApp *self = PHOSH_APP (object);
+  PhoshAppPrivate *priv = phosh_app_get_instance_private(self);
+
+  switch (property_id) {
+  case PHOSH_APP_PROP_APP_ID:
+    g_free (priv->app_id);
+    priv->app_id = g_value_dup_string (value);
+    g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_APP_PROP_APP_ID]);
+    break;
+  case PHOSH_APP_PROP_TITLE:
+    g_free (priv->title);
+    priv->title = g_value_dup_string (value);
+    g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_APP_PROP_TITLE]);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+
+static void
+phosh_app_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+  PhoshApp *self = PHOSH_APP (object);
+  PhoshAppPrivate *priv = phosh_app_get_instance_private(self);
+
+  switch (property_id) {
+  case PHOSH_APP_PROP_APP_ID:
+    g_value_set_string (value, priv->app_id);
+    break;
+  case PHOSH_APP_PROP_TITLE:
+    g_value_set_string (value, priv->title);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+
+/* In GTK+4 we can just call gtk_image_new_from_gicon since it got rid of the
+   restrictive size element */
+static GtkWidget *
+get_image_from_gicon (PhoshApp *self, int scale)
+{
+  PhoshAppPrivate *priv = phosh_app_get_instance_private (self);
+  GtkIconTheme *theme = gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
+  GIcon *icon;
+  GError *err = NULL;
+  g_autoptr(GdkPixbuf) pixbuf = NULL;
+  g_autoptr(GtkIconInfo) info = NULL;
+
+  icon = g_app_info_get_icon (G_APP_INFO (priv->info));
+  info = gtk_icon_theme_lookup_by_gicon_for_scale(theme, icon, 128, scale, 0);
+  if (!info) {
+    g_warning ("Failed to lookup icon for %s", priv->app_id);
+    return NULL;
+  }
+  pixbuf = gtk_icon_info_load_icon (info, &err);
+  if (!pixbuf) {
+    g_warning ("Failed to load icon for %s: %s", priv->app_id, err->message);
+    return NULL;
+  }
+  return gtk_image_new_from_pixbuf (pixbuf);
+}
+
+
+static GtkWidget *
+get_missing_image (int scale)
+{
+  GtkIconTheme *theme = gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
+  GError *err = NULL;
+  g_autoptr(GdkPixbuf) pixbuf = NULL;
+  g_autoptr(GtkIconInfo) info = NULL;
+
+  info = gtk_icon_theme_lookup_icon_for_scale(theme, "image-missing", 128, scale, 0);
+  pixbuf = gtk_icon_info_load_icon (info, &err);
+  return gtk_image_new_from_pixbuf (pixbuf);
+}
+
+
+static void
+phosh_app_constructed (GObject *object)
+{
+  PhoshApp *self = PHOSH_APP (object);
+  PhoshAppPrivate *priv = phosh_app_get_instance_private (self);
+  GtkWidget *lbl_name = NULL, *lbl_title = NULL;
+  g_autofree gchar *desktop_id = NULL;
+  g_autofree gchar *name = NULL;
+  gint scale = 1;
+  PhoshMonitor *monitor = phosh_shell_get_primary_monitor (phosh_shell_get_default());
+
+  if (monitor)
+    scale = monitor->scale;
+
+  desktop_id = g_strdup_printf ("%s.desktop", priv->app_id);
+  g_return_if_fail (desktop_id);
+  priv->info = g_desktop_app_info_new (desktop_id);
+  if (priv->info) {
+    priv->image = get_image_from_gicon (self, scale);
+    name = g_desktop_app_info_get_locale_string (priv->info, "Name");
+    lbl_name = gtk_label_new (name ? name : priv->app_id);
+  } else {
+    lbl_name = gtk_label_new (priv->app_id);
+  }
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(lbl_name)),
+                               "phosh-app-name");
+
+  if (!priv->info || !priv->image) {
+    priv->image = get_missing_image (scale);
+  }
+
+  if (priv->title) {
+      lbl_title = gtk_label_new (priv->title);
+      gtk_label_set_max_width_chars (GTK_LABEL (lbl_title), 30);
+      gtk_label_set_ellipsize (GTK_LABEL (lbl_title), PANGO_ELLIPSIZE_MIDDLE);
+      gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(lbl_title)),
+                                   "phosh-app-title");
+  }
+
+  priv->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_box_pack_start (GTK_BOX (priv->box), priv->image, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (priv->box), lbl_name, FALSE, FALSE, 0);
+  if (lbl_title) {
+    gtk_box_pack_start (GTK_BOX (priv->box), lbl_title, FALSE, FALSE, 0);
+  }
+  gtk_container_add (GTK_CONTAINER (self), priv->box);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(self)),
+                               "phosh-app-btn");
+
+  G_OBJECT_CLASS (phosh_app_parent_class)->constructed (object);
+}
+
+
+static void
+phosh_app_dispose (GObject *object)
+{
+  PhoshApp *self = PHOSH_APP (object);
+  PhoshAppPrivate *priv = phosh_app_get_instance_private (self);
+
+  g_clear_object (&priv->info);
+
+  G_OBJECT_CLASS (phosh_app_parent_class)->dispose (object);
+}
+
+
+static void
+phosh_app_finalize (GObject *object)
+{
+  PhoshApp *self = PHOSH_APP (object);
+  PhoshAppPrivate *priv = phosh_app_get_instance_private (self);
+
+  g_free (priv->app_id);
+  g_free (priv->title);
+
+  G_OBJECT_CLASS (phosh_app_parent_class)->finalize (object);
+}
+
+
+
+static void
+phosh_app_class_init (PhoshAppClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *)klass;
+
+  object_class->constructed = phosh_app_constructed;
+  object_class->dispose = phosh_app_dispose;
+  object_class->finalize = phosh_app_finalize;
+
+  object_class->set_property = phosh_app_set_property;
+  object_class->get_property = phosh_app_get_property;
+
+  props[PHOSH_APP_PROP_APP_ID] =
+    g_param_spec_string (
+      "app-id",
+      "app-id",
+      "The application id",
+      "",
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  props[PHOSH_APP_PROP_TITLE] =
+    g_param_spec_string (
+      "title",
+      "title",
+      "The window's title",
+      "",
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, PHOSH_APP_PROP_LAST_PROP, props);
+}
+
+
+static void
+phosh_app_init (PhoshApp *self)
+{
+}
+
+
+GtkWidget *
+phosh_app_new (const char *app_id, const char *title)
+{
+  return g_object_new (PHOSH_TYPE_APP,
+                       "app-id", app_id,
+                       "title", title,
+                       NULL);
+}
+
+
+const char *
+phosh_app_get_app_id (PhoshApp *self)
+{
+  PhoshAppPrivate *priv;
+
+  g_return_val_if_fail (PHOSH_IS_APP (self), NULL);
+  priv = phosh_app_get_instance_private (self);
+
+  return priv->app_id;
+}
+
+
+const char *
+phosh_app_get_title (PhoshApp *self)
+{
+  PhoshAppPrivate *priv;
+
+  g_return_val_if_fail (PHOSH_IS_APP (self), NULL);
+  priv = phosh_app_get_instance_private (self);
+
+  return priv->title;
+}
diff --git a/src/app.h b/src/app.h
new file mode 100644
index 0000000000000000000000000000000000000000..46c3e357c8ae93d1aaec6ab2c2e5564f4bff438f
--- /dev/null
+++ b/src/app.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ *
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define PHOSH_TYPE_APP (phosh_app_get_type())
+
+G_DECLARE_FINAL_TYPE (PhoshApp, phosh_app, PHOSH, APP, GtkButton)
+
+GtkWidget * phosh_app_new (const char *app_id, const char *title);
+const char *phosh_app_get_app_id (PhoshApp *self);
+const char *phosh_app_get_title (PhoshApp *self);
+
diff --git a/src/meson.build b/src/meson.build
index 3ba0023ee56d886c49fc559bf7d1d4d5b3667b9a..f7eeef181a6d346e82f7eb195a0a7968d0e9eb8e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,6 +9,8 @@ phosh_resources = gnome.compile_resources(
 )
 
 phosh_sources = [
+  'app.c',
+  'app.h',
   'auth.c',
   'auth.h',
   'background.c',
diff --git a/src/style.css b/src/style.css
index 75d220133ce86ecb7005d40b1201a54917e9c1bb..2e344de0104f278980ca71f05a0a2d50bc174a26 100644
--- a/src/style.css
+++ b/src/style.css
@@ -14,3 +14,19 @@
     border-bottom-left-radius: 10px;
  }
 
+/* Favorites / Home screen */
+.phosh-app-btn {
+    background-image: none;
+    background-color: black;
+    border-width: 3px;
+    border-radius: 10px;
+}
+
+.phosh-app-name {
+    font-size: larger;
+    font-weight: bold;
+}
+
+.phosh-app-title {
+    font-size: larger;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 10c7f5af1c6bc95b303071014ca8c62e42017c2a..64abea6211fc1276316a647dbae8dabe5bd3f051 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -29,6 +29,7 @@ test_deps = [
 ]
 
 tests = [
+  ['app', []],
   ['favorites', [ '@0@/src/app.c'.format(meson.source_root())]]
 ]
 
diff --git a/tests/test-app.c b/tests/test-app.c
new file mode 100644
index 0000000000000000000000000000000000000000..de678af4786b991641ad55c78e960b5449382a2e
--- /dev/null
+++ b/tests/test-app.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * SPDX-License-Identifier: GPL-3.0+
+ * Author: Guido Günther <agx@sigxcpu.org>
+ */
+
+#include "app.h"
+
+static void
+test_phosh_app_new(void)
+{
+  PhoshApp *app = PHOSH_APP (phosh_app_new ("com.example.foo", "bar"));
+  g_assert (app);
+  g_assert_cmpstr (phosh_app_get_app_id (app), ==, "com.example.foo");
+  g_assert_cmpstr (phosh_app_get_title (app), ==, "bar");
+  gtk_widget_destroy (GTK_WIDGET (app));
+}
+
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  gtk_test_init (&argc, &argv, NULL);
+
+  g_test_add_func("/phosh/app/new", test_phosh_app_new);
+  return g_test_run();
+}