diff --git a/packages/presentation/lang/en.json b/packages/presentation/lang/en.json
index 71bc55b089..4b4296b6f4 100644
--- a/packages/presentation/lang/en.json
+++ b/packages/presentation/lang/en.json
@@ -30,6 +30,7 @@
     "MakePrivateDescription": "Only members can see it",
     "Created": "Created",
     "NoResults": "No results to show",
-    "Next": "Next"
+    "Next": "Next",
+    "FailedToPreview": "Failed to preview"
   }
 }
diff --git a/packages/presentation/lang/es.json b/packages/presentation/lang/es.json
index c066edcc82..1f0d61ceb7 100644
--- a/packages/presentation/lang/es.json
+++ b/packages/presentation/lang/es.json
@@ -30,6 +30,7 @@
     "MakePrivateDescription": "Solo los miembros pueden verlo",
     "Created": "Creado",
     "NoResults": "No hay resultados para mostrar",
-    "Next": "Siguiente"
+    "Next": "Siguiente",
+    "FailedToPreview": "Error al previsualizar"
   }
 }
\ No newline at end of file
diff --git a/packages/presentation/lang/pt.json b/packages/presentation/lang/pt.json
index 0bed2dacee..594ec66c3a 100644
--- a/packages/presentation/lang/pt.json
+++ b/packages/presentation/lang/pt.json
@@ -30,6 +30,7 @@
     "MakePrivateDescription": "Apenas os membros podem ver",
     "Created": "Criado",
     "NoResults": "Sem resultados para mostrar",
-    "Next": "Seguinte"
+    "Next": "Seguinte",
+    "FailedToPreview": "Falha ao pré-visualizar"
   }
 }
\ No newline at end of file
diff --git a/packages/presentation/lang/ru.json b/packages/presentation/lang/ru.json
index 42aecb5748..7737ffe33e 100644
--- a/packages/presentation/lang/ru.json
+++ b/packages/presentation/lang/ru.json
@@ -30,6 +30,7 @@
     "MakePrivateDescription": "Только пользователи могут видеть это",
     "Created": "Созданные",
     "NoResults": "Нет результатов",
-    "Next": "Далее"
+    "Next": "Далее",
+    "FailedToPreview": "Ошибка предпросмотра"
   }
 }
diff --git a/packages/presentation/src/components/PDFViewer.svelte b/packages/presentation/src/components/PDFViewer.svelte
index 1b81ad2ca5..4b5e3da591 100644
--- a/packages/presentation/src/components/PDFViewer.svelte
+++ b/packages/presentation/src/components/PDFViewer.svelte
@@ -14,20 +14,21 @@
 -->
 <script lang="ts">
   // import { Doc } from '@hcengineering/core'
-  import { Button, Dialog } from '@hcengineering/ui'
+  import { Button, Dialog, Label, Spinner } from '@hcengineering/ui'
   import { createEventDispatcher, onMount } from 'svelte'
   import presentation from '..'
   import { getFileUrl } from '../utils'
   import Download from './icons/Download.svelte'
   import ActionContext from './ActionContext.svelte'
 
-  export let file: string
+  export let file: string | undefined
   export let name: string
   export let contentType: string | undefined
   // export let popupOptions: PopupOptions
   // export let value: Doc
   export let showIcon = true
   export let fullSize = false
+  export let isLoading = false
 
   const dispatch = createEventDispatcher()
 
@@ -42,7 +43,8 @@
     }
   })
   let download: HTMLAnchorElement
-  $: src = getFileUrl(file, 'full', name)
+  $: src = file === undefined ? '' : getFileUrl(file, 'full', name)
+  $: isImage = contentType !== undefined && contentType.startsWith('image/')
 </script>
 
 <ActionContext context={{ mode: 'browser' }} />
@@ -67,24 +69,36 @@
   </svelte:fragment>
 
   <svelte:fragment slot="utils">
-    <a class="no-line" href={src} download={name} bind:this={download}>
-      <Button
-        icon={Download}
-        kind={'ghost'}
-        on:click={() => {
-          download.click()
-        }}
-        showTooltip={{ label: presentation.string.Download }}
-      />
-    </a>
+    {#if !isLoading && isImage && src !== ''}
+      <a class="no-line" href={src} download={name} bind:this={download}>
+        <Button
+          icon={Download}
+          kind={'ghost'}
+          on:click={() => {
+            download.click()
+          }}
+          showTooltip={{ label: presentation.string.Download }}
+        />
+      </a>
+    {/if}
   </svelte:fragment>
 
-  {#if contentType && contentType.startsWith('image/')}
-    <div class="pdfviewer-content img">
-      <img class="img-fit" {src} alt="" />
-    </div>
+  {#if !isLoading}
+    {#if src === ''}
+      <div class="centered">
+        <Label label={presentation.string.FailedToPreview} />
+      </div>
+    {:else if isImage}
+      <div class="pdfviewer-content img">
+        <img class="img-fit" {src} alt="" />
+      </div>
+    {:else}
+      <iframe class="pdfviewer-content" src={src + '#view=FitH&navpanes=0'} title="" />
+    {/if}
   {:else}
-    <iframe class="pdfviewer-content" src={src + '#view=FitH&navpanes=0'} title="" />
+    <div class="centered">
+      <Spinner size="medium" />
+    </div>
   {/if}
 </Dialog>
 
@@ -124,4 +138,12 @@
     max-height: 100%;
     object-fit: contain;
   }
+  .centered {
+    flex-grow: 1;
+    width: 100;
+    height: 100;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
 </style>
diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts
index 70f8615840..aca8b50ae0 100644
--- a/packages/presentation/src/plugin.ts
+++ b/packages/presentation/src/plugin.ts
@@ -71,7 +71,8 @@ export default plugin(presentationId, {
     OpenInANewTab: '' as IntlString,
     Created: '' as IntlString,
     NoResults: '' as IntlString,
-    Next: '' as IntlString
+    Next: '' as IntlString,
+    FailedToPreview: '' as IntlString
   },
   metadata: {
     RequiredVersion: '' as Metadata<string>,
diff --git a/plugins/guest-resources/src/components/CreatePublicLink.svelte b/plugins/guest-resources/src/components/CreatePublicLink.svelte
index 8663eb7b88..9abaeb0539 100644
--- a/plugins/guest-resources/src/components/CreatePublicLink.svelte
+++ b/plugins/guest-resources/src/components/CreatePublicLink.svelte
@@ -14,7 +14,7 @@
 -->
 <script lang="ts">
   import { Doc, Timestamp } from '@hcengineering/core'
-  import { PublicLink } from '@hcengineering/guest'
+  import { PublicLink, createPublicLink } from '@hcengineering/guest'
   import presentaion, {
     Card,
     MessageBox,
@@ -22,7 +22,7 @@
     createQuery,
     getClient
   } from '@hcengineering/presentation'
-  import { Button, Loading, Location, showPopup, ticker } from '@hcengineering/ui'
+  import { Button, Loading, showPopup, ticker } from '@hcengineering/ui'
   import view from '@hcengineering/view'
   import { getObjectLinkFragment } from '@hcengineering/view-resources'
   import { createEventDispatcher } from 'svelte'
@@ -37,26 +37,11 @@
 
   const dispatch = createEventDispatcher()
 
-  async function createLink (location: Location, revokable: boolean = true): Promise<void> {
-    await client.createDoc(guest.class.PublicLink, guest.space.Links, {
-      attachedTo: value._id,
-      location,
-      revokable,
-      restrictions: {
-        readonly: true,
-        disableNavigation: true,
-        disableActions: true,
-        disableComments: true
-      },
-      url: ''
-    })
-  }
-
   async function generate (object: Doc): Promise<void> {
     const panelComponent = client.getHierarchy().classHierarchyMixin(object._class, view.mixin.ObjectPanel)
     const comp = panelComponent?.component ?? view.component.EditDoc
     const loc = await getObjectLinkFragment(client.getHierarchy(), object, {}, comp)
-    await createLink(loc)
+    await createPublicLink(client, value, loc)
   }
 
   async function checkNeedGenerate (value: Doc, loading: boolean, link: PublicLink | undefined): Promise<void> {
diff --git a/plugins/guest/src/index.ts b/plugins/guest/src/index.ts
index 00a125f16f..f35cefc94b 100644
--- a/plugins/guest/src/index.ts
+++ b/plugins/guest/src/index.ts
@@ -3,6 +3,8 @@ import type { Asset, Plugin } from '@hcengineering/platform'
 import { plugin } from '@hcengineering/platform'
 import { AnyComponent, Location } from '@hcengineering/ui'
 
+export * from './utils'
+
 export interface PublicLink extends Doc {
   attachedTo: Ref<Doc>
   url: string
diff --git a/plugins/guest/src/utils.ts b/plugins/guest/src/utils.ts
new file mode 100644
index 0000000000..41f754920f
--- /dev/null
+++ b/plugins/guest/src/utils.ts
@@ -0,0 +1,28 @@
+//
+// Copyright © 2024 Hardcore Engineering Inc.
+//
+
+import { Doc, TxOperations } from '@hcengineering/core'
+import { type Location } from '@hcengineering/ui'
+
+import guest from './index'
+
+export async function createPublicLink (
+  client: TxOperations,
+  object: Doc,
+  location: Location,
+  revokable: boolean = true
+): Promise<void> {
+  await client.createDoc(guest.class.PublicLink, guest.space.Links, {
+    attachedTo: object._id,
+    location,
+    revokable,
+    restrictions: {
+      readonly: true,
+      disableNavigation: true,
+      disableActions: true,
+      disableComments: true
+    },
+    url: ''
+  })
+}