diff --git a/lib/cms/posts/post.ex b/lib/cms/posts/post.ex
index 3918e97..aaaea4f 100644
--- a/lib/cms/posts/post.ex
+++ b/lib/cms/posts/post.ex
@@ -7,14 +7,14 @@ defmodule CMS.Posts.Post do
   @primary_key {:id, :binary_id, autogenerate: true}
   schema "posts" do
     field :title, :string
-    field :contents, :string
+    field :body, :string
 
     timestamps()
   end
 
   def changeset(%__MODULE__{} = post, attrs \\ %{}) do
     post
-    |> cast(attrs, [:title, :contents])
-    |> validate_required([:contents])
+    |> cast(attrs, [:title, :body])
+    |> validate_required([:body])
   end
 end
diff --git a/lib/cms/statuses.ex b/lib/cms/statuses.ex
new file mode 100644
index 0000000..7f817f9
--- /dev/null
+++ b/lib/cms/statuses.ex
@@ -0,0 +1,31 @@
+defmodule CMS.Statuses do
+  @moduledoc false
+  import Ecto.Query
+
+  alias CMS.Repo
+  alias CMS.Statuses.Status
+
+  def create_status(attrs) do
+    %Status{}
+    |> Status.changeset(attrs)
+    |> Repo.insert()
+  end
+
+  def update_status(status, attrs) do
+    status
+    |> Status.changeset(attrs)
+    |> Repo.update()
+  end
+
+  def get_status!(id) do
+    Repo.get!(Status, id)
+  end
+
+  def list_statuses do
+    query =
+      from status in Status,
+        order_by: [desc: status.inserted_at]
+
+    Repo.all(query)
+  end
+end
diff --git a/lib/cms/statuses/status.ex b/lib/cms/statuses/status.ex
new file mode 100644
index 0000000..00cd5f0
--- /dev/null
+++ b/lib/cms/statuses/status.ex
@@ -0,0 +1,19 @@
+defmodule CMS.Statuses.Status do
+  @moduledoc false
+  use Ecto.Schema
+
+  import Ecto.Changeset
+
+  @primary_key {:id, :binary_id, autogenerate: true}
+  schema "statuses" do
+    field :body
+
+    timestamps()
+  end
+
+  def changeset(%__MODULE__{} = status, attrs \\ %{}) do
+    status
+    |> cast(attrs, [:body])
+    |> validate_required([:body])
+  end
+end
diff --git a/lib/cms_web/components/core_components.ex b/lib/cms_web/components/core_components.ex
index be2c6e5..5cb0e5b 100644
--- a/lib/cms_web/components/core_components.ex
+++ b/lib/cms_web/components/core_components.ex
@@ -52,12 +52,25 @@ defmodule CMSWeb.CoreComponents do
 
   attr :class, :string, default: nil
   attr :field, Phoenix.HTML.FormField, required: true
-  attr :global, :global, include: ~w[required placeholder type]
+  attr :type, :string, default: "text"
+  attr :global, :global, include: ~w[required placeholder]
+
+  def input(%{type: "textarea"} = assigns) do
+    ~H"""
+    <textarea
+      class={["px-2 py-1 border border-gray-400 rounded", @class]}
+      id={@field.id}
+      name={@field.name}
+      {@global}
+    >{@field.value}</textarea>
+    """
+  end
 
   def input(assigns) do
     ~H"""
     <input
-      class={["px-1 py-0.5 border border-gray-400 rounded", @class]}
+      class={["px-2 py-1 border border-gray-400 rounded", @class]}
+      type={@type}
       id={@field.id}
       name={@field.name}
       value={@field.value}
@@ -95,6 +108,7 @@ defmodule CMSWeb.CoreComponents do
 
   attr :format, :string, required: true
   attr :value, :any, default: nil
+  attr :formatter, :atom, default: :default
   attr :global, :global
 
   def timex(%{value: nil} = assigns) do
@@ -106,8 +120,12 @@ defmodule CMSWeb.CoreComponents do
   def timex(assigns) do
     ~H"""
     <time datetime={Timex.format!(@value, "{ISO:Extended:Z}")} {@global}>
-      {Timex.format!(@value, @format)}
+      {Timex.format!(@value, @format, timex_formatter(@formatter))}
     </time>
     """
   end
+
+  defp timex_formatter(formatter) do
+    Module.concat(Timex.Format.DateTime.Formatters, :string.titlecase("#{formatter}"))
+  end
 end
diff --git a/lib/cms_web/components/layouts.ex b/lib/cms_web/components/layouts.ex
index 69b254b..4ceac28 100644
--- a/lib/cms_web/components/layouts.ex
+++ b/lib/cms_web/components/layouts.ex
@@ -11,4 +11,10 @@ defmodule CMSWeb.Layouts do
   use CMSWeb, :html
 
   embed_templates "layouts/*"
+
+  defp post?("/writing/" <> _), do: true
+  defp post?(_), do: false
+
+  defp status?("/microblog/" <> _), do: true
+  defp status?(_), do: false
 end
diff --git a/lib/cms_web/components/layouts/app.html.heex b/lib/cms_web/components/layouts/app.html.heex
index 6ff7da9..0fffe7f 100644
--- a/lib/cms_web/components/layouts/app.html.heex
+++ b/lib/cms_web/components/layouts/app.html.heex
@@ -1,50 +1,53 @@
-<div
-  :if={@admin?}
-  class="sticky top-0 bg-white z-50 flex flex-row justify-between py-1 px-3 md:mb-2 border-b border-gray-200"
->
-  <section class="flex flex-row gap-x-2">
-    <div class="pr-2 border-r border-gray-200">
-      <.a navigate={~p"/admin"} class="font-bold">admin</.a>
-    </div>
-    <nav>
-      <ul class="flex flex-row gap-x-2">
-        <li>
-          <.a href={~p"/admin/microblog/new"}>new status</.a>
-        </li>
-        <li>
-          <.a href={~p"/admin/writing/new"}>new draft</.a>
-        </li>
-      </ul>
-    </nav>
-  </section>
+<div class="sticky top-0 z-50 flex flex-row bg-white justify-between py-1 md:mb-2 border-b border-gray-200">
+  <div class="flex flex-col md:flex-row">
+    <section class="flex flex-row gap-x-2 md:border-r border-gray-200 px-2">
+      <.a href={~p"/"} class="font-bold">sloanelybutsurely.com</.a>
+      <nav>
+        <ul class="flex flex-row gap-x-2">
+          <li>
+            <.a href={~p"/writing"}>writing</.a>
+          </li>
+          <li>
+            <.a href={~p"/microblog"}>microblog</.a>
+          </li>
+        </ul>
+      </nav>
+    </section>
+    <section :if={@admin?} class="flex flex-row gap-x-2 px-2">
+      <nav>
+        <ul class="flex flex-row gap-x-2">
+          <li>
+            <.a navigate={~p"/admin"}>admin</.a>
+          </li>
+          <li>
+            <.a href={~p"/admin/statuses/new"}>new status</.a>
+          </li>
+          <li>
+            <.a href={~p"/admin/posts/new"}>new post</.a>
+          </li>
+          <li :if={post?(@current_path)}>
+            <.a href={~p"/admin/posts/#{@post}"}>edit post</.a>
+          </li>
+          <li :if={status?(@current_path)}>
+            <.a href={~p"/admin/statuses/#{@status}"}>edit status</.a>
+          </li>
+        </ul>
+      </nav>
+    </section>
+  </div>
 
-  <section class="flex flex-row">
-    <.a href={~p"/admin/session/destroy?return_to=#{@current_path}"}>
-      sign out
-    </.a>
-  </section>
-</div>
-<div class="flex flex-col md:flex-row mx-auto max-w-4xl">
-  <section class="flex flex-col p-2 gap-y-1 border-gray-200 border-b md:border-b-0">
-    <.a href={~p"/"} class="font-bold">sloanelybutsurely.com</.a>
-    <nav>
-      <ul>
-        <li>
-          <.a href={~p"/writing"}>writing</.a>
-        </li>
-        <li>
-          <.a href={~p"/microblog"}>microblog</.a>
-        </li>
-      </ul>
-    </nav>
-  </section>
-  <main class="p-2 w-full">
-    {@inner_content}
-  </main>
-</div>
-<div
-  :if={not @admin?}
-  class="fixed right-0 bottom-0 p-2 text-transparent underline hover:text-current"
->
-  <.a href={~p"/sign-in?return_to=#{@current_path}"}>sign in</.a>
+  <.a :if={@admin?} class="px-2" href={~p"/admin/session/destroy?return_to=#{@current_path}"}>
+    sign out
+  </.a>
+  <.a
+    :if={!@admin?}
+    class="px-2 text-transparent hover:text-current"
+    href={~p"/sign-in?return_to=#{@current_path}"}
+  >
+    sign in
+  </.a>
 </div>
+
+<main class="p-2 max-w-2xl mx-auto">
+  {@inner_content}
+</main>
diff --git a/lib/cms_web/controllers/page_controller.ex b/lib/cms_web/controllers/page_controller.ex
index 944507c..ca7d635 100644
--- a/lib/cms_web/controllers/page_controller.ex
+++ b/lib/cms_web/controllers/page_controller.ex
@@ -2,12 +2,15 @@ defmodule CMSWeb.PageController do
   use CMSWeb, :controller
 
   alias CMS.Posts
+  alias CMS.Statuses
 
   def home(conn, _params) do
     posts = Enum.take(Posts.list_posts(), 5)
+    statuses = Enum.take(Statuses.list_statuses(), 10)
 
     conn
     |> assign(:posts, posts)
+    |> assign(:statuses, statuses)
     |> render(:home)
   end
 end
diff --git a/lib/cms_web/controllers/page_html/home.html.heex b/lib/cms_web/controllers/page_html/home.html.heex
index c8f2e27..15a2aa8 100644
--- a/lib/cms_web/controllers/page_html/home.html.heex
+++ b/lib/cms_web/controllers/page_html/home.html.heex
@@ -1,11 +1,42 @@
-<section>
-  <.subtitle>
-    <.a href={~p"/writing"}>writing</.a>
-  </.subtitle>
-</section>
+<div class="flex flex-col gap-y-4">
+  <section>
+    <.title>
+      <.a href={~p"/writing"}>writing</.a>
+    </.title>
+    <ul class="flex flex-col">
+      <li :for={post <- @posts}>
+        <.link href={~p"/writing/#{post}"} class="flex flex-row justify-between group">
+          <span class="group-hover:underline">
+            <%= if post.title do %>
+              {post.title}
+            <% else %>
+              (no title)
+            <% end %>
+          </span>
+          <.timex value={post.inserted_at} format="{YYYY}-{0M}-{0D}" class="text-gray-500" />
+        </.link>
+      </li>
+    </ul>
+  </section>
 
-<section>
-  <.subtitle>
-    <.a href={~p"/microblog"}>microblog</.a>
-  </.subtitle>
-</section>
+  <section>
+    <.title>
+      <.a href={~p"/microblog"}>microblog</.a>
+    </.title>
+    <ul class="flex flex-col">
+      <li :for={status <- @statuses}>
+        <.link href={~p"/microblog/#{status}"} class="flex flex-row justify-between group">
+          <span class="group-hover:underline overflow-hidden text-ellipsis text-nowrap">
+            {status.body}
+          </span>
+          <.timex
+            value={status.inserted_at}
+            format="{relative}"
+            formatter={:relative}
+            class="text-gray-500"
+          />
+        </.link>
+      </li>
+    </ul>
+  </section>
+</div>
diff --git a/lib/cms_web/controllers/post_html/index.html.heex b/lib/cms_web/controllers/post_html/index.html.heex
index 76865e0..f20baf6 100644
--- a/lib/cms_web/controllers/post_html/index.html.heex
+++ b/lib/cms_web/controllers/post_html/index.html.heex
@@ -1,13 +1,15 @@
 <.title>writing</.title>
 <ul class="flex flex-col">
-  <li :for={post <- @posts} class="flex flex-row justify-between">
-    <.a href={~p"/writing/#{post}"}>
-      <%= if post.title do %>
-        {post.title}
-      <% else %>
-        (no title)
-      <% end %>
-    </.a>
-    <.timex value={post.inserted_at} format="{YYYY}-{0M}-{0D}" class="text-gray-500" />
+  <li :for={post <- @posts}>
+    <.link href={~p"/writing/#{post}"} class="flex flex-row justify-between group">
+      <span class="group-hover:underline">
+        <%= if post.title do %>
+          {post.title}
+        <% else %>
+          (no title)
+        <% end %>
+      </span>
+      <.timex value={post.inserted_at} format="{YYYY}-{0M}-{0D}" class="text-gray-500" />
+    </.link>
   </li>
 </ul>
diff --git a/lib/cms_web/controllers/post_html/show.html.heex b/lib/cms_web/controllers/post_html/show.html.heex
index ad7b2a4..991292c 100644
--- a/lib/cms_web/controllers/post_html/show.html.heex
+++ b/lib/cms_web/controllers/post_html/show.html.heex
@@ -2,7 +2,7 @@
   <header class="flex flex-row">
     <h1 :if={@post.title} class="font-bold text-xl mb-3">{@post.title}</h1>
   </header>
-  <section class="prose prose-cms max-w-none">{raw(@post.contents)}</section>
+  <section class="prose prose-cms max-w-none">{raw(@post.body)}</section>
   <footer class="mt-5 py-1 border-t border-gray-200 relative">
     <.link :if={@admin?} class="absolute right-0" href={~p"/admin/posts/#{@post}"}>edit</.link>
   </footer>
diff --git a/lib/cms_web/controllers/status_controller.ex b/lib/cms_web/controllers/status_controller.ex
new file mode 100644
index 0000000..6bf211e
--- /dev/null
+++ b/lib/cms_web/controllers/status_controller.ex
@@ -0,0 +1,21 @@
+defmodule CMSWeb.StatusController do
+  use CMSWeb, :controller
+
+  alias CMS.Statuses
+
+  def index(conn, _params) do
+    statuses = Statuses.list_statuses()
+
+    conn
+    |> assign(:statuses, statuses)
+    |> render(:index)
+  end
+
+  def show(conn, %{"status_id" => status_id}) do
+    status = Statuses.get_status!(status_id)
+
+    conn
+    |> assign(:status, status)
+    |> render(:show)
+  end
+end
diff --git a/lib/cms_web/controllers/status_html.ex b/lib/cms_web/controllers/status_html.ex
new file mode 100644
index 0000000..e92f149
--- /dev/null
+++ b/lib/cms_web/controllers/status_html.ex
@@ -0,0 +1,6 @@
+defmodule CMSWeb.StatusHTML do
+  @moduledoc false
+  use CMSWeb, :html
+
+  embed_templates "status_html/*"
+end
diff --git a/lib/cms_web/controllers/status_html/index.html.heex b/lib/cms_web/controllers/status_html/index.html.heex
new file mode 100644
index 0000000..c3105a9
--- /dev/null
+++ b/lib/cms_web/controllers/status_html/index.html.heex
@@ -0,0 +1,16 @@
+<.title>microblog</.title>
+<ul class="flex flex-col">
+  <li :for={status <- @statuses}>
+    <.link href={~p"/microblog/#{status}"} class="flex flex-row justify-between group">
+      <span class="group-hover:underline overflow-hidden text-ellipsis text-nowrap">
+        {status.body}
+      </span>
+      <.timex
+        value={status.inserted_at}
+        format="{relative}"
+        formatter={:relative}
+        class="text-gray-500"
+      />
+    </.link>
+  </li>
+</ul>
diff --git a/lib/cms_web/controllers/status_html/show.html.heex b/lib/cms_web/controllers/status_html/show.html.heex
new file mode 100644
index 0000000..4b8bdf6
--- /dev/null
+++ b/lib/cms_web/controllers/status_html/show.html.heex
@@ -0,0 +1,3 @@
+<article>
+  <p>{@status.body}</p>
+</article>
diff --git a/lib/cms_web/live/admin_login_live.ex b/lib/cms_web/live/admin_login_live.ex
index a8f9e1c..9af7136 100644
--- a/lib/cms_web/live/admin_login_live.ex
+++ b/lib/cms_web/live/admin_login_live.ex
@@ -19,20 +19,8 @@ defmodule CMSWeb.AdminLoginLive do
     ~H"""
     <main class="flex flex-col w-screen h-screen fixed justify-center items-center">
       <.form for={@form} action={~p"/admin/session/create"} class="flex flex-col gap-y-2">
-        <input
-          type="hidden"
-          id={@form[:return_to].id}
-          name={@form[:return_to].name}
-          value={@form[:return_to].value}
-        />
-        <input
-          type="password"
-          placeholder="password"
-          id={@form[:password].id}
-          name={@form[:password].name}
-          value={@form[:password].value}
-          required
-        />
+        <.input type="hidden" field={@form[:return_to]} />
+        <.input type="password" placeholder="password" field={@form[:password]} required />
         <div class="flex flex-col items-end">
           <button type="submit" class="font-bold hover:underline">sign in</button>
           <.a href={cancel_href(@return_to)}>
diff --git a/lib/cms_web/live/post_live.ex b/lib/cms_web/live/post_live.ex
index 253c608..d47e53a 100644
--- a/lib/cms_web/live/post_live.ex
+++ b/lib/cms_web/live/post_live.ex
@@ -60,10 +60,10 @@ defmodule CMSWeb.PostLive do
   def render(assigns) do
     ~H"""
     <.form for={@form} class="flex flex-col gap-y-2" phx-submit="save_post">
-      <.input type="hidden" field={@form[:contents]} />
+      <.input type="hidden" field={@form[:body]} />
       <.input class="text-lg" field={@form[:title]} placeholder="Title" />
       <div id="editor" phx-update="ignore">
-        <trix-editor input={@form[:contents].id} class="prose prose-cms max-w-none"></trix-editor>
+        <trix-editor input={@form[:body].id} class="prose prose-cms max-w-none"></trix-editor>
       </div>
 
       <button type="submit" class="self-end">save</button>
diff --git a/lib/cms_web/live/status_live.ex b/lib/cms_web/live/status_live.ex
new file mode 100644
index 0000000..036ff97
--- /dev/null
+++ b/lib/cms_web/live/status_live.ex
@@ -0,0 +1,67 @@
+defmodule CMSWeb.StatusLive do
+  @moduledoc false
+  use CMSWeb, :live_view
+
+  alias CMS.Statuses
+  alias CMS.Statuses.Status
+
+  @impl true
+  def mount(_params, _session, socket) do
+    {:ok, socket}
+  end
+
+  @impl true
+  def handle_params(_params, uri, %{assigns: %{live_action: :new}} = socket) do
+    status = %Status{}
+
+    changeset = Status.changeset(status)
+
+    socket = assign(socket, status: status, form: to_form(changeset))
+
+    {:noreply, socket}
+  end
+
+  def handle_params(%{"status_id" => status_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do
+    status = Statuses.get_status!(status_id)
+
+    changeset = Status.changeset(status)
+
+    socket = assign(socket, status: status, form: to_form(changeset))
+
+    {:noreply, socket}
+  end
+
+  @impl true
+  def handle_event("save_status", %{"status" => attrs}, %{assigns: %{live_action: :new}} = socket) do
+    socket =
+      case Statuses.create_status(attrs) do
+        {:ok, status} -> push_navigate(socket, to: ~p"/admin/statuses/#{status}")
+        {:error, changeset} -> assign(socket, form: to_form(changeset))
+      end
+
+    {:noreply, socket}
+  end
+
+  def handle_event("save_status", %{"status" => attrs}, %{assigns: %{status: status, live_action: :edit}} = socket) do
+    socket =
+      case Statuses.update_status(status, attrs) do
+        {:ok, status} ->
+          assign(socket, status: status, form: to_form(Status.changeset(status)))
+
+        {:error, changeset} ->
+          assign(socket, form: to_form(changeset))
+      end
+
+    {:noreply, socket}
+  end
+
+  @impl true
+  def render(assigns) do
+    ~H"""
+    <.form for={@form} class="flex flex-col gap-y-2" phx-submit="save_status">
+      <.input type="textarea" field={@form[:body]} />
+      <button type="submit" class="self-end">save</button>
+    </.form>
+    """
+  end
+end
diff --git a/lib/cms_web/router.ex b/lib/cms_web/router.ex
index 699cb21..4d87d96 100644
--- a/lib/cms_web/router.ex
+++ b/lib/cms_web/router.ex
@@ -36,6 +36,9 @@ defmodule CMSWeb.Router do
       get "/writing", PostController, :index
       get "/writing/:post_id", PostController, :show
 
+      get "/microblog", StatusController, :index
+      get "/microblog/:status_id", StatusController, :show
+
       live "/sign-in", AdminLoginLive
       post "/admin/session/create", AdminSessionController, :create
       get "/admin/session/destroy", AdminSessionController, :destroy
@@ -47,11 +50,11 @@ defmodule CMSWeb.Router do
 
       live "/", AdminLive
 
-      live "/writing/new", PostLive, :new
-      live "/writing/:post_id", PostLive, :edit
+      live "/posts/new", PostLive, :new
+      live "/posts/:post_id", PostLive, :edit
 
-      live "/microblog/new", StatusLive, :new
-      live "/microblog/:status_id", StatusLive, :edit
+      live "/statuses/new", StatusLive, :new
+      live "/statuses/:status_id", StatusLive, :edit
     end
   end
 
diff --git a/priv/repo/migrations/20250222164951_add_posts_table.exs b/priv/repo/migrations/20250222164951_add_posts_table.exs
index 065b85c..e424163 100644
--- a/priv/repo/migrations/20250222164951_add_posts_table.exs
+++ b/priv/repo/migrations/20250222164951_add_posts_table.exs
@@ -5,7 +5,7 @@ defmodule CMS.Repo.Migrations.AddPostsTable do
     create table(:posts, primary_key: false) do
       add :id, :uuid, primary_key: true
       add :title, :text
-      add :contents, :text, null: false, default: ""
+      add :body, :text, null: false, default: ""
 
       timestamps()
     end
diff --git a/priv/repo/migrations/20250222201807_add_statuses_table.exs b/priv/repo/migrations/20250222201807_add_statuses_table.exs
new file mode 100644
index 0000000..e57fab2
--- /dev/null
+++ b/priv/repo/migrations/20250222201807_add_statuses_table.exs
@@ -0,0 +1,14 @@
+defmodule CMS.Repo.Migrations.AddStatusesTable do
+  use Ecto.Migration
+
+  def change do
+    create table(:statuses, primary_key: false) do
+      add :id, :uuid, primary_key: true
+      add :body, :text, null: false
+
+      timestamps()
+    end
+
+    create index(:statuses, [:inserted_at])
+  end
+end