diff --git a/packages/app/src/components/Searcher/index.jsx b/packages/app/src/components/Searcher/index.jsx
new file mode 100644
index 00000000..7e31a484
--- /dev/null
+++ b/packages/app/src/components/Searcher/index.jsx
@@ -0,0 +1,157 @@
+import React from "react"
+import * as antd from "antd"
+import classnames from "classnames"
+
+import { UserPreview } from "components"
+import { Icons, createIconRender } from "components/Icons"
+
+import "./index.less"
+
+const ResultRenders = {
+ users: (props) => {
+ const { item, onClick } = props
+
+ return
+
+
+ }
+}
+
+const ResultsTypeDecorators = {
+ users: {
+ icon: "Users",
+ label: "Users"
+ }
+}
+
+const Results = (props) => {
+ let { results } = props
+
+ if (!results) {
+ return
+ }
+
+ if (typeof results !== "object") {
+ return
+ }
+
+ let keys = Object.keys(results)
+
+ if (keys.length === 0) {
+ return
+ }
+
+ // check if all keys are valid, if not replace as "others"
+ keys = keys.map((type) => {
+ if (ResultRenders[type]) {
+ return type
+ }
+
+ return "others"
+ })
+
+ const handleOnClick = (type, value) => {
+ if (typeof props.onClick !== "function") {
+ console.warn("Searcher: onClick is not a function")
+ return
+ }
+
+ return props.onClick(type, value)
+ }
+
+ return keys.map((type) => {
+ const decorator = ResultsTypeDecorators[type] ?? {
+ label: keys,
+ icon:
+ }
+
+ return
+
{createIconRender(decorator.icon)}{decorator.label}
+
+ {
+ results[type].map((item) => {
+ return React.createElement(ResultRenders[type], { item, onClick: (...props) => handleOnClick(type, ...props) })
+ })
+ }
+
+
+ })
+}
+
+export default (props) => {
+ const [loading, setLoading] = React.useState(false)
+ const [searchResult, setSearchResult] = React.useState(null)
+ const [searchValue, setSearchValue] = React.useState("")
+
+ const makeSearch = async (value) => {
+ if (value === "") {
+ return setSearchResult(null)
+ }
+
+ const result = await app.searchEngine.search(value)
+
+ return setSearchResult(result)
+ }
+
+ const handleOnSearch = (e) => {
+ // not allow to input space as first character
+ if (e.target.value[0] === " ") {
+ return
+ }
+
+ setSearchValue(e.target.value)
+ }
+
+ const handleResultClick = (type, value) => {
+ switch (type) {
+ case "users": {
+ app.goToAccount(value.username)
+ break
+ }
+ case "posts": {
+ app.goToPost(value)
+ break
+ }
+
+ default: {
+ console.warn("Searcher: cannot handle clicks on result of type :", type)
+ break
+ }
+ }
+
+ if (typeof props.close === "function") {
+ props.close()
+ }
+ }
+
+ React.useEffect(() => {
+ const timer = setTimeout(async () => {
+ setLoading(true)
+ await makeSearch(searchValue)
+ setLoading(false)
+ }, 400)
+
+ return () => clearTimeout(timer)
+ }, [searchValue])
+
+ return
+
}
+ autoFocus
+ />
+
+ {searchResult &&
+ {loading &&
}
+ {
+ !loading &&
+ }
+
}
+
+}
\ No newline at end of file
diff --git a/packages/app/src/components/Searcher/index.less b/packages/app/src/components/Searcher/index.less
new file mode 100644
index 00000000..139f2f49
--- /dev/null
+++ b/packages/app/src/components/Searcher/index.less
@@ -0,0 +1,90 @@
+.searcher {
+ display: flex;
+ flex-direction: column;
+
+ align-items: center;
+ justify-content: center;
+
+ transition: all 0.3s ease-in-out;
+
+ .ant-input-affix-wrapper {
+ border: 0;
+ padding: 0;
+ margin: 0;
+
+ height: 60px;
+ border-radius: 10px;
+
+ padding: 0 10px;
+
+ .ant-input-prefix {
+ font-size: 2rem;
+
+ svg {
+ color: var(--text-color);
+ }
+ }
+
+ .ant-input {
+ height: 100%;
+ color: var(--text-color);
+
+ font-size: 1rem;
+ }
+ }
+
+ .results {
+ display: flex;
+ flex-direction: column;
+
+ align-items: flex-start;
+ justify-content: center;
+
+ width: 100%;
+
+ padding: 10px;
+ margin-top: 20px;
+
+ background-color: var(--background-color-primary);
+
+ border-radius: 8px;
+
+ .category {
+ width: 100%;
+
+ margin-bottom: 20px;
+
+ h3 {
+ font-size: 2rem;
+ font-weight: 600;
+
+ margin-bottom: 10px;
+ }
+
+ .suggestions {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ width: 90%;
+
+ margin: auto;
+
+ .suggestion {
+ width: 100%;
+
+ margin-bottom: 10px;
+ padding: 10px;
+
+ border-radius: 8px;
+
+ background-color: var(--background-color-accent);
+ }
+ }
+ }
+ }
+
+ .ant-empty {
+ align-self: center;
+ }
+}
\ No newline at end of file