diff --git a/components/nav/NavSide.vue b/components/nav/NavSide.vue
index 59ef211f..2de4f545 100644
--- a/components/nav/NavSide.vue
+++ b/components/nav/NavSide.vue
@@ -4,26 +4,22 @@ const { notifications } = useNotifications()
 
 <template>
   <nav sm:px3 sm:py4 flex="~ col gap2" text-size-base leading-normal md:text-lg>
-    <template v-if="isMastoInitialised && currentUser">
-      <NavSideItem :text="$t('nav_side.home')" to="/home" icon="i-ri:home-5-line" />
-      <NavSideItem :text="$t('nav_side.notifications')" to="/notifications" icon="i-ri:notification-4-line">
-        <template #icon>
-          <div flex relative>
-            <div class="i-ri:notification-4-line" md:text-size-inherit text-xl />
-            <div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
-              {{ notifications < 10 ? notifications : '•' }}
-            </div>
+    <NavSideItem :text="$t('nav_side.home')" to="/home" icon="i-ri:home-5-line" />
+    <NavSideItem :text="$t('nav_side.notifications')" to="/notifications" icon="i-ri:notification-4-line" :user-only="true">
+      <template #icon>
+        <div flex relative>
+          <div class="i-ri:notification-4-line" md:text-size-inherit text-xl />
+          <div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
+            {{ notifications < 10 ? notifications : '•' }}
           </div>
-        </template>
-      </NavSideItem>
-    </template>
+        </div>
+      </template>
+    </NavSideItem>
     <NavSideItem :text="$t('nav_side.explore')" :to="`/${currentServer}/explore`" icon="i-ri:hashtag" />
     <NavSideItem :text="$t('nav_side.local')" :to="`/${currentServer}/public/local`" icon="i-ri:group-2-line " />
     <NavSideItem :text="$t('nav_side.federated')" :to="`/${currentServer}/public`" icon="i-ri:earth-line" />
-    <template v-if="isMastoInitialised && currentUser">
-      <NavSideItem :text="$t('nav_side.conversations')" to="/conversations" icon="i-ri:at-line" />
-      <NavSideItem :text="$t('nav_side.favourites')" to="/favourites" icon="i-ri:heart-3-line" />
-      <NavSideItem :text="$t('nav_side.bookmarks')" to="/bookmarks" icon="i-ri:bookmark-line " />
-    </template>
+    <NavSideItem :text="$t('nav_side.conversations')" to="/conversations" icon="i-ri:at-line" :user-only="true" />
+    <NavSideItem :text="$t('nav_side.favourites')" to="/favourites" icon="i-ri:heart-3-line" :user-only="true" />
+    <NavSideItem :text="$t('nav_side.bookmarks')" to="/bookmarks" icon="i-ri:bookmark-line " :user-only="true" />
   </nav>
 </template>
diff --git a/components/nav/NavSideItem.vue b/components/nav/NavSideItem.vue
index 58e07834..be5e25b8 100644
--- a/components/nav/NavSideItem.vue
+++ b/components/nav/NavSideItem.vue
@@ -1,9 +1,12 @@
 <script setup lang="ts">
-const props = defineProps<{
+const props = withDefaults(defineProps<{
   text?: string
   icon: string
   to: string | Record<string, string>
-}>()
+  userOnly?: boolean
+}>(), {
+  userOnly: false,
+})
 
 defineSlots<{
   icon: {}
@@ -22,10 +25,26 @@ useCommand({
     router.push(props.to)
   },
 })
+
+let activeClass = $ref('text-primary')
+watch(isMastoInitialised, async () => {
+  if (!props.userOnly) {
+    // TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
+    // we don't have currentServer defined until later
+    activeClass = ''
+    await nextTick()
+    activeClass = 'text-primary'
+  }
+})
+
+// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
+// when we know there is no user.
+const noUserDisable = computed(() => !isMastoInitialised.value || (props.userOnly && !currentUser.value))
+const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly && !currentUser.value)
 </script>
 
 <template>
-  <NuxtLink :to="to" :active-class="isMastoInitialised ? 'text-primary' : ''" group focus:outline-none @click="$scrollToTop">
+  <NuxtLink :to="to" :disabled="noUserDisable" :class="noUserVisual ? 'op25 pointer-events-none ' : ''" :active-class="activeClass" group focus:outline-none @click="$scrollToTop">
     <CommonTooltip :disabled="!isMediumScreen" :content="text" placement="right">
       <div flex w-fit px2 mx3 lg:mx0 lg:px5 py2 gap4 items-center transition-100 rounded-full group-hover:bg-active group-focus-visible:ring="2 current">
         <slot name="icon">
diff --git a/components/publish/PublishButton.vue b/components/publish/PublishButton.vue
index f182d0c8..d1150a47 100644
--- a/components/publish/PublishButton.vue
+++ b/components/publish/PublishButton.vue
@@ -1,5 +1,16 @@
+<script setup>
+const disabled = computed(() => !isMastoInitialised.value || !currentUser.value)
+const disabledVisual = computed(() => isMastoInitialised.value && !currentUser.value)
+</script>
+
 <template>
-  <button color-primary btn-outline rounded-full ml-7 lg:ml-3 w-9 lg:w-auto font-bold py2 lg:py4 flex="~ gap2 center" @click="openPublishDialog()">
+  <button
+    color-primary rounded-full ml-7 lg:ml-3 w-9 lg:w-auto font-bold py2 lg:py4 flex="~ gap2 center"
+    cursor-pointer disabled:pointer-events-none
+    text-primary border-1 border-primary
+    :disabled="disabled" :class="disabledVisual ? 'op25' : 'hover:bg-primary hover:text-inverted'"
+    @click="openPublishDialog()"
+  >
     <div i-ri:quill-pen-line />
     <span hidden lg:block>{{ $t('action.compose') }}</span>
   </button>
diff --git a/layouts/default.vue b/layouts/default.vue
index 08d795f1..2efbe01b 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -11,7 +11,7 @@
             <div flex="~ col" overflow-y-auto justify-between h-full>
               <div flex flex-col>
                 <NavSide />
-                <PublishButton v-if="isMastoInitialised && currentUser" m5 />
+                <PublishButton m5 />
               </div>
               <div flex flex-col>
                 <UserSignInEntry v-if="isMastoInitialised && !currentUser" sm:hidden />