How to use server function & suspense

I will be frank, I have some experience with React but this is my first dive into Solid coming from Astro I'm trying to have a fallback loading component, that is replaced by data fetched from the server when available I figured the best way to do this was an asynchronous server function, I am not claiming there is a bug I think I just did something wrong Here is my high-level code since I can't really share more:
async function getUserInfo() {
"use server";
// Mimics server speed, network latency, etc.
await new Promise((resolve) => setTimeout(resolve, 300));

// TO-DO: real data / connections here
return {
avatar: "~/assets/img/placeholders/generic-avatar.jpg",
name: "User Name",
};
}

//... component defined
const [fetchedInfo] = createResource(getUserInfo);

return (
<Suspense fallback={<UserInfoFallback />}>
<img
class="aspect-square size-full"
src={fetchedInfo()?.avatar}
decoding="sync"
loading="eager"
alt="User Avatar"
draggable="false"
/>
<span class="text-neutral-content flex w-full flex-col gap-0.5 truncate text-left">
<p class="block truncate text-sm font-semibold">
{fetchedInfo()?.name}
</p>
<p class="block truncate text-[0.7rem] font-light">
{fetchedInfo()?.email}
</p>
</span>
</Suspense> )
async function getUserInfo() {
"use server";
// Mimics server speed, network latency, etc.
await new Promise((resolve) => setTimeout(resolve, 300));

// TO-DO: real data / connections here
return {
avatar: "~/assets/img/placeholders/generic-avatar.jpg",
name: "User Name",
};
}

//... component defined
const [fetchedInfo] = createResource(getUserInfo);

return (
<Suspense fallback={<UserInfoFallback />}>
<img
class="aspect-square size-full"
src={fetchedInfo()?.avatar}
decoding="sync"
loading="eager"
alt="User Avatar"
draggable="false"
/>
<span class="text-neutral-content flex w-full flex-col gap-0.5 truncate text-left">
<p class="block truncate text-sm font-semibold">
{fetchedInfo()?.name}
</p>
<p class="block truncate text-[0.7rem] font-light">
{fetchedInfo()?.email}
</p>
</span>
</Suspense> )
7 Replies
ⱼ ₒ ₑ
ⱼ ₒ ₑOP2mo ago
In the future there will likely be API calls and it will be implemented differently but I'm just trying to grasp basic ideas ATM
Brendonovich
Brendonovich2mo ago
What's the problem you're having? The only wrong thing i can see is passing the ~/assets/img string directly into src
ⱼ ₒ ₑ
ⱼ ₒ ₑOP2mo ago
This doesn't work? It just gives a "Script error" and no discernable output for me to work with
peerreynders
peerreynders2mo ago
Just to be clear you are using SolidStart (which is required for "use server" RPC)? Usually people throw the SolidStart tag onto the topic.
ⱼ ₒ ₑ
ⱼ ₒ ₑOP2mo ago
Yes Sorry I should have done that
peerreynders
peerreynders2mo ago
- Created fresh SolidStart(TS) "basic" template - Dropped the following into src/routes/about.tsx - Just works.
// file: src/routes/about.tsx
import { Suspense, Show } from 'solid-js';
import { createAsync } from '@solidjs/router';
import { Title } from '@solidjs/meta';

async function getUserInfo() {
'use server';
await new Promise((resolve) => setTimeout(resolve, 300));

console.log('getUserInfo', new Date().toString());
return {
avatar: 'https://placehold.co/100x100/93c5fd/ffffff?text=Generic Avatar',
name: 'User Name',
};
}

export default function About() {
const fetchedInfo = createAsync(getUserInfo);

return (
<main>
<Title>About</Title>
<h1>About</h1>
<Suspense fallback={'loading'}>
<Show when={fetchedInfo()}>
{(user) => (
<>
<img
src={user().avatar}
decoding="sync"
loading="eager"
alt="User Avatar"
draggable="false"
/>
<span>
<p>{user().name}</p>
<p>{user().email}</p>
</span>
</>
)}
</Show>
</Suspense>
</main>
);
}
// file: src/routes/about.tsx
import { Suspense, Show } from 'solid-js';
import { createAsync } from '@solidjs/router';
import { Title } from '@solidjs/meta';

async function getUserInfo() {
'use server';
await new Promise((resolve) => setTimeout(resolve, 300));

console.log('getUserInfo', new Date().toString());
return {
avatar: 'https://placehold.co/100x100/93c5fd/ffffff?text=Generic Avatar',
name: 'User Name',
};
}

export default function About() {
const fetchedInfo = createAsync(getUserInfo);

return (
<main>
<Title>About</Title>
<h1>About</h1>
<Suspense fallback={'loading'}>
<Show when={fetchedInfo()}>
{(user) => (
<>
<img
src={user().avatar}
decoding="sync"
loading="eager"
alt="User Avatar"
draggable="false"
/>
<span>
<p>{user().name}</p>
<p>{user().email}</p>
</span>
</>
)}
</Show>
</Suspense>
</main>
);
}
ⱼ ₒ ₑ
ⱼ ₒ ₑOP2mo ago
TY

Did you find this page helpful?