diff --git a/src/components/Root.js b/src/components/Root.tsx
similarity index 68%
rename from src/components/Root.js
rename to src/components/Root.tsx
index 8c06100b7a1249f9ff24a4a894d1c7fcf86039b9..adb76a72e684025e56ae3da8f9cd4a08e262c8d3 100644
--- a/src/components/Root.js
+++ b/src/components/Root.tsx
@@ -1,9 +1,11 @@
 import './Root.css';
 
 import React, { lazy, Suspense } from 'react';
+import { PartialRouteObject } from 'react-router';
 import { HashRouter as Router, useRoutes } from 'react-router-dom';
 import { RecoilRoot } from 'recoil';
 import { About } from 'src/components/about/About';
+import { Head } from 'src/components/shared/Head';
 
 import { actions, initialState } from '../store';
 import APIConfig from './APIConfig';
@@ -16,40 +18,45 @@ import SideBar from './SideBar';
 import StateProvider from './StateProvider';
 import StyleGuide from './StyleGuide';
 
-const Connections = lazy(() =>
-  import(
-    /* webpackChunkName: "conns" */
-    /* webpackPrefetch: true */
-    './Connections'
-  )
+const Connections = lazy(
+  () =>
+    import(
+      /* webpackChunkName: "conns" */
+      /* webpackPrefetch: true */
+      './Connections'
+    )
 );
-const Config = lazy(() =>
-  import(
-    /* webpackChunkName: "config" */
-    /* webpackPrefetch: true */
-    './Config'
-  )
+const Config = lazy(
+  () =>
+    import(
+      /* webpackChunkName: "config" */
+      /* webpackPrefetch: true */
+      './Config'
+    )
 );
-const Logs = lazy(() =>
-  import(
-    /* webpackChunkName: "logs" */
-    /* webpackPrefetch: true */
-    './Logs'
-  )
+const Logs = lazy(
+  () =>
+    import(
+      /* webpackChunkName: "logs" */
+      /* webpackPrefetch: true */
+      './Logs'
+    )
 );
-const Proxies = lazy(() =>
-  import(
-    /* webpackChunkName: "proxies" */
-    /* webpackPrefetch: true */
-    './proxies/Proxies'
-  )
+const Proxies = lazy(
+  () =>
+    import(
+      /* webpackChunkName: "proxies" */
+      /* webpackPrefetch: true */
+      './proxies/Proxies'
+    )
 );
-const Rules = lazy(() =>
-  import(
-    /* webpackChunkName: "rules" */
-    /* webpackPrefetch: true */
-    './Rules'
-  )
+const Rules = lazy(
+  () =>
+    import(
+      /* webpackChunkName: "rules" */
+      /* webpackPrefetch: true */
+      './Rules'
+    )
 );
 
 const routes = [
@@ -61,7 +68,7 @@ const routes = [
   { path: '/rules', element: <Rules /> },
   { path: '/about', element: <About /> },
   __DEV__ ? { path: '/style', element: <StyleGuide /> } : false,
-].filter(Boolean);
+].filter(Boolean) as PartialRouteObject[];
 
 function RouteInnerApp() {
   return useRoutes(routes);
@@ -94,6 +101,7 @@ const Root = () => (
       <StateProvider initialState={initialState} actions={actions}>
         <Router>
           <div className={s0.app}>
+            <Head />
             <Suspense fallback={<Loading2 />}>
               <App />
             </Suspense>
diff --git a/src/components/shared/Head.tsx b/src/components/shared/Head.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5997a3a8d78d855b9c6794314e59771db01c2eed
--- /dev/null
+++ b/src/components/shared/Head.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react';
+import { Helmet } from 'react-helmet';
+import { connect } from 'src/components/StateProvider';
+import { getClashAPIConfig } from 'src/store/app';
+
+const mapState = (s) => ({
+  apiConfig: getClashAPIConfig(s),
+});
+
+function HeadImpl({ apiConfig }: { apiConfig: { baseURL: string } }) {
+  let title = 'yacd';
+  try {
+    title = new URL(apiConfig.baseURL).host;
+  } catch (e) {
+    // ignore
+  }
+  return (
+    <Helmet>
+      <title>{title}</title>
+    </Helmet>
+  );
+}
+
+export const Head = connect(mapState)(HeadImpl);