diff --git a/components/nav/NavFooter.vue b/components/nav/NavFooter.vue
index a35e516e..6b251684 100644
--- a/components/nav/NavFooter.vue
+++ b/components/nav/NavFooter.vue
@@ -1,11 +1,10 @@
 <script setup lang="ts">
-const buildTime = import.meta.env.__BUILD_TIME__ as string
-const buildCommit = import.meta.env.__BUILD_COMMIT__ as string
-const buildTimeDate = new Date(buildTime)
+import buildInfo from 'virtual:build-info'
 
 const timeAgoOptions = useTimeAgoOptions()
 
-const buildTimeAgo = useTimeAgo(buildTime, timeAgoOptions)
+const buildTimeDate = new Date(buildInfo.time)
+const buildTimeAgo = useTimeAgo(buildTimeDate, timeAgoOptions)
 </script>
 
 <template>
@@ -53,17 +52,21 @@ const buildTimeAgo = useTimeAgo(buildTime, timeAgoOptions)
     <div>{{ $t('app_desc_short') }}</div>
     <div>
       <i18n-t keypath="nav_footer.built_at">
-        <time :datetime="buildTime" :title="$d(buildTimeDate, 'long')">{{ buildTimeAgo }}</time>
+        <time :datetime="String(buildTimeDate)" :title="$d(buildTimeDate, 'long')">{{ buildTimeAgo }}</time>
       </i18n-t>
-      <template v-if="buildCommit">
+      <template v-if="buildInfo.version">
+        &middot;
+        v{{ buildInfo.version }}
+      </template>
+      <template v-if="buildInfo.commit && buildInfo.branch !== 'release'">
         &middot;
         <NuxtLink
           external
-          :href="`https://github.com/elk-zone/elk/commit/${buildCommit}`"
+          :href="`https://github.com/elk-zone/elk/commit/${buildInfo.commit}`"
           target="_blank"
           font-mono
         >
-          {{ buildCommit.slice(0, 7) }}
+          {{ buildInfo.commit.slice(0, 7) }}
         </NuxtLink>
       </template>
     </div>
diff --git a/modules/build-info.ts b/modules/build-info.ts
new file mode 100644
index 00000000..7da46d4e
--- /dev/null
+++ b/modules/build-info.ts
@@ -0,0 +1,30 @@
+import { addVitePlugin, defineNuxtModule } from '@nuxt/kit'
+import Git from 'simple-git'
+import { version } from '../package.json'
+import type { BuildInfo } from '~/types'
+
+export default defineNuxtModule({
+  meta: {
+    name: 'elk:build-info',
+  },
+  async setup() {
+    const git = Git()
+    const buildInfo: BuildInfo = {
+      version,
+      time: +Date.now(),
+      commit: await git.revparse(['HEAD']),
+      branch: await git.revparse(['--abbrev-ref', 'HEAD']),
+    }
+    addVitePlugin({
+      name: 'elk:build-info',
+      resolveId(id) {
+        if (id === 'virtual:build-info')
+          return id
+      },
+      load(id) {
+        if (id === 'virtual:build-info')
+          return `export default ${JSON.stringify(buildInfo, null, 2)}`
+      },
+    })
+  },
+})
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 059fed96..b10b5ff7 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -20,6 +20,7 @@ export default defineNuxtConfig({
     '@nuxtjs/i18n',
     '~/modules/purge-comments',
     '~/modules/setup-components',
+    '~/modules/build-info',
     '~/modules/pwa/index', // change to '@vite-pwa/nuxt' once released and remove pwa module
     '~/modules/tauri/index',
   ],
@@ -45,8 +46,6 @@ export default defineNuxtConfig({
   },
   vite: {
     define: {
-      'import.meta.env.__BUILD_TIME__': JSON.stringify(new Date().toISOString()),
-      'import.meta.env.__BUILD_COMMIT__': JSON.stringify(process.env.COMMIT_REF || ''),
       'process.env.VSCODE_TEXTMATE_DEBUG': 'false',
       'process.mock': ((!isCI || isPreview) && process.env.MOCK_USER) || 'false',
     },
diff --git a/shims.d.ts b/shims.d.ts
index e82d75f0..9d70010c 100644
--- a/shims.d.ts
+++ b/shims.d.ts
@@ -1,3 +1,9 @@
 /// <reference types="@types/wicg-file-system-access" />
 /// <reference types="vite-plugin-pwa/info" />
 /// <reference types="vite-plugin-pwa/client" />
+
+declare module 'virtual:build-info' {
+  import type { BuildInfo } from '~/types'
+  const buildInfo: BuildInfo
+  export default buildInfo
+}
diff --git a/styles/global.css b/styles/global.css
index d269f57e..e007448c 100644
--- a/styles/global.css
+++ b/styles/global.css
@@ -184,3 +184,9 @@ body {
 em-emoji-picker {
   --border-radius: 0;
 }
+
+footer {
+  a {
+    --at-apply: 'hover:underline';
+  }
+}
diff --git a/types/index.ts b/types/index.ts
index c1b5f852..383d27e2 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -65,3 +65,10 @@ export interface Draft {
   attachments: Attachment[]
 }
 export type DraftMap = Record<string, Draft>
+
+export interface BuildInfo {
+  version: string
+  commit: string
+  time: number
+  branch: string
+}