diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..1923d4101aa52161105a6f9d54e29f68ace4b19b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/package.json b/package.json
index 45a1787108f036d8611f765fb210c214da6aedc5..585c6a78c78f9865ec20317e6be8523fe9ae0475 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
     "classnames": "^2.2.6",
     "date-fns": "^2.16.1",
     "keycloak-js": "^10.0.2",
+    "lodash": "^4.17.20",
     "pullstate": "^1.20.4",
     "react": "^16.13.1",
     "react-device-detect": "^1.13.1",
@@ -51,7 +52,7 @@
               "^@?\\w"
             ],
             [
-              "^(components|containers|pages|utils)(/.*|$)"
+              "^(components|containers|pages|utils|stores|keycloak)(/.*|$)"
             ],
             [
               "^(test-utils)(/.*|$)"
diff --git a/src/App.jsx b/src/App.jsx
index a2e7648d1891d535e0e300b8148168de18524f14..848f3da3ad726e9583dce37352d6817469205177 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,13 +1,14 @@
-import React, { useCallback, useEffect } from "react";
+import React, { Suspense } from "react";
 import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
 import { KeycloakProvider } from "@react-keycloak/web";
 import * as Sentry from "@sentry/react";
-import { AuthStore } from "stores";
+import { loadGroupMappings } from "actions/misc";
 
 import Footer from "components/Footer";
 import Navbar from "components/Navbar";
 import Home from "pages/Home";
 import Program from "pages/Program";
+import { AuthStore } from "stores";
 
 import keycloak from "./keycloak";
 
@@ -30,6 +31,7 @@ const onKeycloakEvent = (event) => {
       state.user = {
         name: keycloak.tokenParsed.name,
         groups: keycloak.tokenParsed.groups,
+        accessToken: keycloak.token,
       };
     });
   }
@@ -50,18 +52,7 @@ const LoadingComponent = (
 );
 
 const BaseApp = () => {
-  const loadGroupMappings = useCallback(async () => {
-    const resp = await fetch("https://iapi.pirati.cz/v1/groups");
-    const mappings = await resp.json();
-
-    AuthStore.update((state) => {
-      state.groupMappings = mappings;
-    });
-  }, []);
-
-  useEffect(() => {
-    loadGroupMappings();
-  }, [loadGroupMappings]);
+  loadGroupMappings.read();
 
   return (
     <Router>
@@ -91,29 +82,33 @@ const AuthenticatedApp = () => {
         LoadingComponent={LoadingComponent}
         onEvent={onKeycloakEvent}
       >
-        <BaseApp />
+        <Suspense fallback={LoadingComponent}>
+          <BaseApp />
+        </Suspense>
       </KeycloakProvider>
     </>
   );
 };
 
-const ErrorBoundaryFallback = () => (
-  <div className="h-screen w-screen flex justify-center items-center">
-    <div className="text-center">
-      <h1 className="head-alt-xl text-red-600 mb-4">
-        V aplikaci došlo k chybě :(
-      </h1>
-      <p className="text-lg leading-normal">
-        Naši vývojáři o tom již byli informování a opraví to co nejdříve.
-        <br />
-        Omlouváme se za tuto nepříjemnost.
-      </p>
-      <a href="/" className="btn mt-8">
-        <div className="btn__body">Načíst znovu</div>
-      </a>
+const ErrorBoundaryFallback = ({ error }) => {
+  return (
+    <div className="h-screen w-screen flex justify-center items-center">
+      <div className="text-center">
+        <h1 className="head-alt-xl text-red-600 mb-4">
+          V aplikaci došlo k chybě :(
+        </h1>
+        <p className="text-lg leading-normal">
+          Naši vývojáři o tom již byli informování a opraví to co nejdříve.
+          <br />
+          Omlouváme se za tuto nepříjemnost.
+        </p>
+        <a href="/" className="btn mt-8">
+          <div className="btn__body">Načíst znovu</div>
+        </a>
+      </div>
     </div>
-  </div>
-);
+  );
+};
 
 const App = Sentry.withProfiler(() => {
   return (
diff --git a/src/actions/misc.js b/src/actions/misc.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe88505c298c04652412b0bd52861bd22420dc96
--- /dev/null
+++ b/src/actions/misc.js
@@ -0,0 +1,24 @@
+import { createAsyncAction, errorResult, successResult } from "pullstate";
+
+import { AuthStore } from "stores";
+
+export const loadGroupMappings = createAsyncAction(
+  async () => {
+    try {
+      const resp = await fetch("https://iapi.pirati.cz/v1/groups");
+      const mappings = await resp.json();
+      return successResult(mappings);
+    } catch (err) {
+      return errorResult([], err.toString());
+    }
+  },
+  {
+    postActionHook: ({ result }) => {
+      if (!result.error) {
+        AuthStore.update((state) => {
+          state.groupMappings = result.payload;
+        });
+      }
+    },
+  }
+);
diff --git a/src/components/Chip.jsx b/src/components/Chip.jsx
index dd4603bf47980eeb6c49a5036c19690aaacfdb5d..4e9895cc1f8d2aa00d0a80658882f2fe9d0ad0a0 100644
--- a/src/components/Chip.jsx
+++ b/src/components/Chip.jsx
@@ -1,17 +1,29 @@
 import React from "react";
 import classNames from "classnames";
 
-const Chip = ({ className, color = "grey-125", condensed, children }) => {
+const Chip = ({
+  className,
+  color = "grey-125",
+  condensed,
+  hoveractive = false,
+  children,
+  ...props
+}) => {
   const chipClass = classNames(
     "chip",
     {
       "chip--condensed": !!condensed,
+      "chip--hoveractive": !!hoveractive,
     },
     `chip--${color}`,
     className
   );
 
-  return <span className={chipClass}>{children}</span>;
+  return (
+    <span className={chipClass} {...props}>
+      {children}
+    </span>
+  );
 };
 
 export default Chip;
diff --git a/src/components/Dropdown.jsx b/src/components/Dropdown.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..f7e1a49e0d0bdf8ad1f7bac134c2ba373462be34
--- /dev/null
+++ b/src/components/Dropdown.jsx
@@ -0,0 +1,28 @@
+import React from "react";
+import classNames from "classnames";
+
+const Dropdown = ({ value, options, onChange, className }) => {
+  const onSelectChanged = (evt) => {
+    onChange(evt.target.value);
+  };
+
+  return (
+    <span
+      className={classNames(
+        "chip chip--grey-125 chip--select chip--hoveractive",
+        className
+      )}
+    >
+      <select onChange={onSelectChanged} value={value}>
+        {options.map((opt) => (
+          <option key={opt.value} value={opt.value}>
+            {opt.title}
+          </option>
+        ))}
+      </select>
+      <span className="chip__icon ico--chevron-down"></span>
+    </span>
+  );
+};
+
+export default Dropdown;
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index 838698c82636fb171aef3bb12ad7551e38147ac0..1a908b253eefb0329e987cf8ecd23ad73dbe431e 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -2,9 +2,9 @@ import React, { useCallback, useState } from "react";
 import { isBrowser } from "react-device-detect";
 import { NavLink } from "react-router-dom";
 import { useKeycloak } from "@react-keycloak/web";
-import { AuthStore } from "stores";
 
 import Button from "components/Button";
+import { AuthStore } from "stores";
 
 const Navbar = () => {
   const [showMenu, setShowMenu] = useState(isBrowser);
diff --git a/src/components/Thumbs.jsx b/src/components/Thumbs.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c267eeee91f4b075f3e59c0401d90bedf14480c7
--- /dev/null
+++ b/src/components/Thumbs.jsx
@@ -0,0 +1,33 @@
+import React from "react";
+import classNames from "classnames";
+
+const Thumbs = ({ likes, dislikes, onLike, onDislike, canThumb }) => {
+  return (
+    <div>
+      <div className="space-x-2 text-sm flex items-center">
+        <button
+          className={classNames("text-blue-300 flex items-center space-x-1", {
+            "cursor-not-allowed": !canThumb,
+          })}
+          disabled={!canThumb}
+          onClick={onLike}
+        >
+          <span className="font-bold">{likes}</span>
+          <i className="ico--thumbs-up"></i>
+        </button>
+        <button
+          className={classNames("text-red-600 flex items-center space-x-1", {
+            "cursor-not-allowed": !canThumb,
+          })}
+          disabled={!canThumb}
+          onClick={onDislike}
+        >
+          <i className="ico--thumbs-down transform -scale-x-1"></i>
+          <span className="font-bold">{dislikes}</span>
+        </button>
+      </div>
+    </div>
+  );
+};
+
+export default React.memo(Thumbs);
diff --git a/src/components/annoucements/Announcement.jsx b/src/components/annoucements/Announcement.jsx
index 39b769b8b62e16aef917a7f8df92c54dfd4c8d89..392f7da5a512c1810d3c3eb80e13f2790d0c976d 100644
--- a/src/components/annoucements/Announcement.jsx
+++ b/src/components/annoucements/Announcement.jsx
@@ -34,7 +34,7 @@ const Announcement = ({
     "accepted-procedure-proposal": "green-400",
     voting: "red-600",
     announcement: "cyan-500",
-    "user-bank": "black",
+    "user-ban": "black",
   }[type];
 
   const chipLabel = {
diff --git a/src/components/posts/Post.jsx b/src/components/posts/Post.jsx
index 2e8a892e5a27d66ecd0e322aeb18b380fe1165b4..675642415e283cfb7d8e6722bbb0a007acbd6b27 100644
--- a/src/components/posts/Post.jsx
+++ b/src/components/posts/Post.jsx
@@ -3,6 +3,7 @@ import classNames from "classnames";
 import { format } from "date-fns";
 
 import Chip from "components/Chip";
+import Thumbs from "components/Thumbs";
 
 const Post = ({
   className,
@@ -15,12 +16,16 @@ const Post = ({
   archived,
   state,
   historyLog,
+  onLike,
+  onDislike,
+  dimIfArchived = true,
 }) => {
   const wrapperClassName = classNames(
-    "flex items-start p-4 lg:p-2 lg:py-4 lg:-mx-2",
+    "flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2",
     {
       "bg-yellow-100 bg-opacity-50": !seen,
-      "opacity-25 hover:opacity-100 transition-opacity duration-200": !!archived,
+      "opacity-25 hover:opacity-100 transition-opacity duration-200":
+        dimIfArchived && !!archived,
     },
     className
   );
@@ -127,16 +132,13 @@ const Post = ({
               </div>
             </div>
             <div className="flex items-center space-x-4">
-              <div className="space-x-2 text-sm flex items-center">
-                <button className="text-blue-300 flex items-center space-x-1">
-                  <span className="font-bold">{ranking.likes}</span>
-                  <i className="ico--thumbs-up"></i>
-                </button>
-                <button className="text-red-600 flex items-center space-x-1">
-                  <i className="ico--thumbs-down transform -scale-x-1"></i>
-                  <span className="font-bold">{ranking.dislikes}</span>
-                </button>
-              </div>
+              <Thumbs
+                likes={ranking.likes}
+                dislikes={ranking.dislikes}
+                onLike={onLike}
+                onDislike={onDislike}
+                canThumb={ranking.myVote === "none"}
+              />
             </div>
           </div>
         </div>
diff --git a/src/components/posts/PostFilters.jsx b/src/components/posts/PostFilters.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..376cd32c4b70d1ecde6d7b9ad893e01ab1bc5042
--- /dev/null
+++ b/src/components/posts/PostFilters.jsx
@@ -0,0 +1,114 @@
+import React, { useCallback } from "react";
+import pick from "lodash/pick";
+
+import Chip from "components/Chip";
+import Dropdown from "components/Dropdown";
+import { PostStore } from "stores";
+import { filterPosts } from "utils";
+
+const PostFilters = () => {
+  const { window, filters, items } = PostStore.useState((state) =>
+    pick(state, ["window", "filters", "items"])
+  );
+
+  const flagsOptions = [
+    { title: "Vše", value: "all" },
+    { title: "Jen aktivní", value: "active" },
+    { title: "Jen archivované", value: "archived" },
+  ];
+  const sortOptions = [
+    { title: "Podle času", value: "byDate" },
+    { title: "Podle podpory", value: "byScore" },
+  ];
+  const typeOptions = [
+    { title: "Návrhy i příspěvky", value: "all" },
+    { title: "Jen návrhy", value: "proposalsOnly" },
+    { title: "Jen příspěvky", value: "discussionOnly" },
+  ];
+  const hasNextPage = window.page * window.perPage < window.itemCount;
+  const hasPrevPage = window.page > 1;
+
+  const setFilter = (prop, newValue, resetPage = true) => {
+    PostStore.update((state) => {
+      state.filters[prop] = newValue;
+      state.window.items = filterPosts(state.filters, items);
+      state.window.itemCount = state.window.items.length;
+
+      if (resetPage) {
+        state.window.page = 1;
+      }
+    });
+  };
+
+  const onFlagsChange = (newValue) => setFilter("flags", newValue);
+  const onSortChange = (newValue) => setFilter("sort", newValue, false);
+  const onTypeChange = (newValue) => setFilter("type", newValue);
+
+  const onNextPage = useCallback(() => {
+    if (hasNextPage) {
+      PostStore.update((state) => {
+        state.window.page = state.window.page + 1;
+      });
+    }
+  }, [hasNextPage]);
+  const onPrevPage = useCallback(() => {
+    if (hasPrevPage) {
+      PostStore.update((state) => {
+        state.window.page = state.window.page - 1;
+      });
+    }
+  }, [hasPrevPage]);
+
+  const enabledPaginatorClass = "cursor-pointer text-xs";
+  const disabledPaginatorClass = "opacity-25 cursor-not-allowed text-xs";
+
+  return (
+    <div className="flex flex-col space-y-2 xl:space-y-0 xl:space-x-8 xl:flex-row xl:items-center">
+      <div className="-mx-1">
+        <Dropdown
+          value={filters.flags}
+          onChange={onFlagsChange}
+          options={flagsOptions}
+          className="text-xs ml-1 mt-2 xl:mt-0"
+        />
+        <Dropdown
+          value={filters.sort}
+          onChange={onSortChange}
+          options={sortOptions}
+          className="text-xs ml-1 mt-2 xl:mt-0"
+        />
+        <Dropdown
+          value={filters.type}
+          onChange={onTypeChange}
+          options={typeOptions}
+          className="text-xs ml-1 mt-2 xl:mt-0"
+        />
+      </div>
+
+      <div>
+        <Chip
+          color="grey-125"
+          className={
+            hasPrevPage ? enabledPaginatorClass : disabledPaginatorClass
+          }
+          hoveractive
+          onClick={onPrevPage}
+        >
+          <span className="ico--chevron-left"></span>
+        </Chip>
+        <Chip
+          color="grey-125"
+          className={
+            hasNextPage ? enabledPaginatorClass : disabledPaginatorClass
+          }
+          hoveractive
+          onClick={onNextPage}
+        >
+          <span className="ico--chevron-right"></span>
+        </Chip>
+      </div>
+    </div>
+  );
+};
+
+export default PostFilters;
diff --git a/src/components/posts/PostList.jsx b/src/components/posts/PostList.jsx
index efdb9ccc332f773edeb11c8efd9211a02508f261..b76ebc4212649e3101a3c80f68f8499b21701a0f 100644
--- a/src/components/posts/PostList.jsx
+++ b/src/components/posts/PostList.jsx
@@ -3,7 +3,18 @@ import classNames from "classnames";
 
 import Post from "./Post";
 
-const PostList = ({ className, items }) => {
+const PostList = ({ className, items, onLike, onDislike, dimArchived }) => {
+  const onPostLike = (post) => {
+    return (evt) => {
+      onLike(post);
+    };
+  };
+  const onPostDislike = (post) => {
+    return (evt) => {
+      onDislike(post);
+    };
+  };
+
   return (
     <div className={classNames("space-y-px", className)}>
       {items
@@ -20,6 +31,9 @@ const PostList = ({ className, items }) => {
             historyLog={item.historyLog}
             seen={item.seen}
             archived={item.archived}
+            onLike={onPostLike(item)}
+            onDislike={onPostDislike(item)}
+            dimIfArchived={dimArchived}
           />
         ))}
     </div>
diff --git a/src/containers/AnnoucementsContainer.jsx b/src/containers/AnnoucementsContainer.jsx
index f75e8ff0efee520f418ef2cac24116670adbf1c5..a2937a3360051dcfa04cca0d6eb0a60836411159 100644
--- a/src/containers/AnnoucementsContainer.jsx
+++ b/src/containers/AnnoucementsContainer.jsx
@@ -1,59 +1,10 @@
 import React from "react";
 
 import AnnouncementList from "components/annoucements/AnnouncementList";
+import { AnnouncementStore } from "stores";
 
 const AnnoucementsContainer = () => {
-  /** @type {CF2021.Announcement[]} */
-  const items = [
-    {
-      id: "1",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: false,
-      type: "rejected-procedure-proposal",
-    },
-    {
-      id: "2",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: false,
-      type: "accepted-procedure-proposal",
-    },
-    {
-      id: "3",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: true,
-      type: "suggested-procedure-proposal",
-    },
-    {
-      id: "4",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: true,
-      type: "voting",
-    },
-    {
-      id: "5",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: true,
-      type: "announcement",
-    },
-    {
-      id: "6",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      seen: true,
-      type: "user-ban",
-    },
-  ];
+  const items = AnnouncementStore.useState((state) => state.items);
 
   return <AnnouncementList items={items} />;
 };
diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx
index caeea1740f1456a8cff25338f746166ca8f5a058..b0166c73b3ebf791cf6d6845e9476adb1e7c4db1 100644
--- a/src/containers/PostsContainer.jsx
+++ b/src/containers/PostsContainer.jsx
@@ -1,224 +1,29 @@
 import React from "react";
 
 import PostList from "components/posts/PostList";
+import { PostStore } from "stores";
 
 const PostsContainer = ({ className }) => {
-  /** @type {CF2021.Post[]} */
-  const items = [
-    {
-      id: "1",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "cf",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: false,
-      archived: false,
-      type: "post",
-    },
-    {
-      id: "2",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "cf",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: false,
-      archived: false,
-      type: "post",
-    },
-    {
-      id: "3",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "cf",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "post",
-    },
-    {
-      id: "4",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "post",
-    },
-    {
-      id: "5",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "post",
-    },
-    {
-      id: "6",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: true,
-      type: "post",
-    },
-    {
-      id: "7",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: true,
-      type: "procedure-proposal",
-      state: "pending",
-    },
-    {
-      id: "8",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "procedure-proposal",
-      state: "announced",
-      historyLog: [
-        {
-          attribute: "content",
-          datetime: new Date(),
-          newValue: "Lemme know",
-          originator: "chairman",
-        },
-      ],
-    },
-    {
-      id: "9",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "procedure-proposal",
-      state: "accepted",
-    },
-    {
-      id: "10",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: false,
-      type: "procedure-proposal",
-      state: "rejected",
-    },
-    {
-      id: "11",
-      content:
-        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
-      datetime: new Date(),
-      author: {
-        name: "John Doe",
-        group: "KS Pardubický kraj",
-      },
-      ranking: {
-        likes: 5,
-        dislikes: 1,
-        score: 4,
-      },
-      seen: true,
-      archived: true,
-      type: "procedure-proposal",
-      state: "rejected-by-chairman",
-    },
-  ];
+  const window = PostStore.useState((state) => state.window);
+  const showingArchivedOnly = PostStore.useState(
+    (state) => state.filters.flags === "archived"
+  );
 
-  return <PostList items={items} className={className} />;
+  const onLike = (post) => console.log("like", post);
+  const onDislike = (post) => console.log("dislike", post);
+
+  const sliceStart = (window.page - 1) * window.perPage;
+  const sliceEnd = window.page * window.perPage;
+
+  return (
+    <PostList
+      items={window.items.slice(sliceStart, sliceEnd)}
+      onLike={onLike}
+      onDislike={onDislike}
+      className={className}
+      dimArchived={!showingArchivedOnly}
+    />
+  );
 };
 
 export default PostsContainer;
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
index 48c1b810db89a09893ea049933e39166995af831..964f3354c9cf08537951e506165d4e91e73365c5 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.jsx
@@ -1,5 +1,6 @@
 import React from "react";
 
+import PostFilters from "components/posts/PostFilters";
 import AnnouncementsContainer from "containers/AnnoucementsContainer";
 import PostsContainer from "containers/PostsContainer";
 
@@ -60,44 +61,7 @@ const Home = () => {
             <h2 className="head-heavy-sm whitespace-no-wrap">
               Příspěvky v rozpravě
             </h2>
-            <div className="flex flex-col space-y-2 xl:space-y-0 xl:space-x-8 xl:flex-row xl:items-center">
-              <div className="-mx-1">
-                <span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
-                  <select>
-                    <option value="">Jen nezpracované</option>
-                    <option value="">Vše</option>
-                    <option value="">Jen aktivní</option>
-                    <option value="">Jen archivované</option>
-                  </select>
-                  <span className="chip__icon ico--chevron-down"></span>
-                </span>
-                <span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
-                  <select>
-                    <option value="">Podle času</option>
-                    <option value="">Podle podpory</option>
-                  </select>
-                  <span className="chip__icon ico--chevron-down"></span>
-                </span>
-                <span className="chip chip--grey-125 chip--select chip--hoveractive text-xs ml-1 mt-2 xl:mt-0">
-                  <select>
-                    <option value="">Návrhy i příspěvky</option>
-                    <option value="">Jen návrhy</option>
-                    <option value="">Jen příspěvky</option>
-                  </select>
-                  <span className="chip__icon ico--chevron-down"></span>
-                </span>
-              </div>
-
-              <div>
-                <span className="chip chip--grey-125 chip--hoveractive text-xs">
-                  <span className="ico--chevron-left"></span>
-                </span>
-
-                <span className="chip chip--grey-125 chip--hoveractive ml-1">
-                  <span className="ico--chevron-right"></span>
-                </span>
-              </div>
-            </div>
+            <PostFilters />
           </div>
 
           <PostsContainer className="container-padding--zero lg:container-padding--auto" />
diff --git a/src/stores.js b/src/stores.js
index cda15d259c0460aa45970bd53b9f877473dc9df8..58f433b89e31c122a84d9e3f6b7d778a3fd192fa 100644
--- a/src/stores.js
+++ b/src/stores.js
@@ -1,5 +1,8 @@
+import memoize from "lodash/memoize";
 import { Store } from "pullstate";
 
+import { filterPosts } from "utils";
+
 /** @type {CF2021.AuthStorePayload} */
 const authStoreInitial = {
   isAuthenticated: false,
@@ -10,14 +13,315 @@ export const AuthStore = new Store(authStoreInitial);
 
 /** @type {CF2021.AnnouncementStorePayload} */
 const announcementStoreInitial = {
-  items: [],
+  items: [
+    {
+      id: "1",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: false,
+      type: "rejected-procedure-proposal",
+    },
+    {
+      id: "2",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: false,
+      type: "accepted-procedure-proposal",
+    },
+    {
+      id: "3",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: true,
+      type: "suggested-procedure-proposal",
+    },
+    {
+      id: "4",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: true,
+      type: "voting",
+    },
+    {
+      id: "5",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: true,
+      type: "announcement",
+    },
+    {
+      id: "6",
+      content:
+        "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+      datetime: new Date(),
+      seen: true,
+      type: "user-ban",
+    },
+  ],
 };
 
 export const AnnouncementStore = new Store(announcementStoreInitial);
 
+const allPosts = [
+  {
+    id: "1",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "cf",
+    },
+    ranking: {
+      likes: 0,
+      dislikes: 0,
+      score: 0,
+      myVote: "none",
+    },
+    seen: false,
+    archived: false,
+    type: "post",
+  },
+  {
+    id: "2",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "cf",
+    },
+    ranking: {
+      likes: 1,
+      dislikes: 0,
+      score: 1,
+      myVote: "none",
+    },
+    seen: false,
+    archived: false,
+    type: "post",
+  },
+  {
+    id: "3",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "cf",
+    },
+    ranking: {
+      likes: 5,
+      dislikes: 5,
+      score: 0,
+      myVote: "none",
+    },
+    seen: true,
+    archived: false,
+    type: "post",
+  },
+  {
+    id: "4",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 0,
+      dislikes: 10,
+      score: -10,
+      myVote: "none",
+    },
+    seen: true,
+    archived: false,
+    type: "post",
+  },
+  {
+    id: "5",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 1,
+      dislikes: 1,
+      score: 0,
+      myVote: "none",
+    },
+    seen: true,
+    archived: false,
+    type: "post",
+  },
+  {
+    id: "6",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 5,
+      dislikes: 3,
+      score: 2,
+      myVote: "none",
+    },
+    seen: true,
+    archived: true,
+    type: "post",
+  },
+  {
+    id: "7",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 5,
+      dislikes: 8,
+      score: -3,
+      myVote: "none",
+    },
+    seen: true,
+    archived: true,
+    type: "procedure-proposal",
+    state: "pending",
+  },
+  {
+    id: "8",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 2,
+      dislikes: 1,
+      score: 1,
+      myVote: "like",
+    },
+    seen: true,
+    archived: false,
+    type: "procedure-proposal",
+    state: "announced",
+    historyLog: [
+      {
+        attribute: "content",
+        datetime: new Date(),
+        newValue: "Lemme know",
+        originator: "chairman",
+      },
+    ],
+  },
+  {
+    id: "9",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 5,
+      dislikes: 0,
+      score: 5,
+      myVote: "dislike",
+    },
+    seen: true,
+    archived: false,
+    type: "procedure-proposal",
+    state: "accepted",
+  },
+  {
+    id: "10",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 5,
+      dislikes: 8,
+      score: -3,
+      myVote: "none",
+    },
+    seen: true,
+    archived: false,
+    type: "procedure-proposal",
+    state: "rejected",
+  },
+  {
+    id: "11",
+    content:
+      "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.",
+    datetime: new Date(),
+    author: {
+      name: "John Doe",
+      group: "KS Pardubický kraj",
+    },
+    ranking: {
+      likes: 10,
+      dislikes: 1,
+      score: 9,
+      myVote: "none",
+    },
+    seen: true,
+    archived: true,
+    type: "procedure-proposal",
+    state: "rejected-by-chairman",
+  },
+];
+
+const initialPostFilters = {
+  flags: "all",
+  sort: "byDate",
+  type: "all",
+};
+
+const filteredPosts = filterPosts(initialPostFilters, allPosts);
+
 /** @type {CF2021.PostStorePayload} */
 const postStoreInitial = {
-  items: [],
+  items: allPosts,
+  itemCount: allPosts.length,
+  window: {
+    items: filteredPosts,
+    itemCount: filteredPosts.length,
+    page: 1,
+    perPage: 5,
+  },
+  filters: initialPostFilters,
 };
 
 export const PostStore = new Store(postStoreInitial);
+
+export const getGroupByCode = memoize(
+  (groupMappings, groupCode) => {
+    return groupMappings.find((gm) => gm.code === groupCode);
+  },
+  (groupMappings, groupCode) => [groupMappings, groupCode]
+);
+
+export const getGroupsByCode = memoize((groupMappings, groupCodes) => {
+  return groupCodes.map((code) => getGroupByCode(groupMappings, code));
+});
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e9bd0904343707d1b9e75fb99fe1c166be04db2
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,27 @@
+import filter from "lodash/filter";
+
+export const filterPosts = (filters, allItems) => {
+  const predicate = {};
+
+  if (filters.flags === "active") {
+    predicate.archived = false;
+  }
+  if (filters.flags === "archived") {
+    predicate.archived = true;
+  }
+
+  if (filters.type === "proposalsOnly") {
+    predicate.type = "procedure-proposal";
+  }
+  if (filters.type === "discussionOnly") {
+    predicate.type = "post";
+  }
+
+  let filteredItems = filter(allItems, predicate);
+
+  if (filters.sort === "byDate") {
+    return filteredItems.sort((a, b) => b.datetime - a.datetime);
+  }
+
+  return filteredItems.sort((a, b) => b.ranking.score - a.ranking.score);
+};
diff --git a/typings/cf2021.d.ts b/typings/cf2021.d.ts
index 5df54146d88029a954d035d05bdfa30f26ca75f5..b560ff8dae57fe00d5cdc12ae24ddbbcbd77104a 100644
--- a/typings/cf2021.d.ts
+++ b/typings/cf2021.d.ts
@@ -29,6 +29,7 @@ declare namespace CF2021 {
         user: {
             name: string;
             groups: string[];
+            accessToken: string;
         };
     }
 
@@ -73,6 +74,7 @@ declare namespace CF2021 {
             score: number;
             likes: number;
             dislikes: number;
+            myVote: "like" | "dislike" | "none";
         };
         historyLog: {
             attribute: string;
@@ -104,5 +106,17 @@ declare namespace CF2021 {
 
     export interface PostStorePayload {
         items: Post[];
+        itemCount: number;
+        window: {
+          items: Post[];
+          itemCount: number;
+          page: number;
+          perPage: number;
+        };
+        filters: {
+          flags: "all" | "active" | "archived";
+          sort: "byDate" | "byScore";
+          type: "all" | "proposalsOnly" | "discussionOnly";
+        };
     }
 }