Frequently Asked Questions

Product Information & Getting Started

What is Hygraph and how does it work with SvelteKit?

Hygraph is a GraphQL-native headless CMS that enables developers and content creators to build, manage, and deliver structured content efficiently. In the context of SvelteKit, Hygraph provides a flexible backend for storing timeline data, which can be queried via its GraphQL API and rendered on the frontend using SvelteKit components. The integration allows for dynamic, personalized timelines and content-driven web applications. For more details, see the official guide.

How can I get started with Hygraph?

You can get started easily by signing up for a free-forever account at Hygraph. Comprehensive resources such as documentation, video tutorials, and onboarding guides are available to help you navigate the platform and build your first project.

What is the primary purpose of Hygraph?

Hygraph's primary purpose is to unify data and enable content federation, allowing businesses and developers to create impactful digital experiences. Its GraphQL-native architecture removes traditional content management pain points, offering scalability, flexibility, and efficient data querying. Learn more.

Features & Capabilities

What are the key features of Hygraph?

Hygraph offers a GraphQL-native architecture, content federation, scalability, and a user-friendly interface. It supports rapid content delivery, integrations with popular platforms (e.g., Netlify, Vercel, Shopify, AWS S3), and provides enterprise-grade security. For a full list, visit Hygraph Features.

Does Hygraph provide an API for content management?

Yes, Hygraph provides a powerful GraphQL API for efficient content fetching and management. You can learn more at the API Reference.

What integrations does Hygraph support?

Hygraph supports a wide range of integrations, including Netlify, Vercel, BigCommerce, Shopify, Lokalise, AWS S3, Cloudinary, Mux, Ninetailed, AltText.ai, and more. For the full list, visit Hygraph Integrations.

How does Hygraph optimize content delivery performance?

Hygraph emphasizes optimized content delivery performance, ensuring rapid content distribution and responsiveness. This leads to improved user experience, higher engagement, and better search engine rankings. For more details, visit this page.

Pricing & Plans

What is Hygraph's pricing model?

Hygraph offers a free forever Hobby plan, a Growth plan starting at $199/month, and custom Enterprise plans. For more details, visit the pricing page.

Security & Compliance

What security and compliance certifications does Hygraph have?

Hygraph is SOC 2 Type 2 compliant, ISO 27001 certified, and GDPR compliant. It offers features like SSO integrations, audit logs, encryption at rest and in transit, and sandbox environments to protect sensitive data and meet regulatory standards. For more details, visit Hygraph Security Features.

Technical Implementation & Documentation

Where can I find technical documentation for Hygraph?

Comprehensive technical documentation is available at Hygraph Documentation, covering everything from setup to advanced integrations.

Where can I find the code for building a personal timeline with Hygraph and SvelteKit?

The code for the personal timeline project is available on GitHub. You can also find more examples at Hygraph Examples.

How does Hygraph integrate with Svelte for building a personal timeline?

Hygraph integrates with Svelte by using components such as Basics and Activities. You can import these components and pass necessary props like avatar, name, bio, and activityDetails to render timeline data dynamically. For more details, see the guide.

Use Cases & Benefits

Who can benefit from using Hygraph?

Hygraph is ideal for developers, IT decision-makers, content creators, project/program managers, agencies, solution partners, and technology partners. It is especially beneficial for modern software companies, enterprises looking to modernize their tech stack, and brands aiming to scale across geographies or improve development velocity. See case studies.

What business impact can customers expect from using Hygraph?

Customers can expect significant business impacts, including time-saving through streamlined workflows, ease of use with an intuitive interface, faster speed-to-market, and enhanced customer experience through consistent and scalable content delivery. These benefits help businesses modernize their tech stack and achieve operational efficiency. Learn more.

Pain Points & Solutions

What problems does Hygraph solve?

Hygraph addresses operational pains (reliance on developers for content updates, outdated tech stacks, conflicting needs from global teams, clunky user experiences), financial pains (high operational costs, slow speed-to-market, expensive maintenance, scalability challenges), and technical pains (boilerplate code, overwhelming queries, evolving schemas, cache problems, OpenID integration challenges). Learn more.

How does Hygraph solve these pain points?

Hygraph provides an intuitive interface for non-technical users, modernizes outdated systems with its GraphQL-native, API-first architecture, ensures consistent branding across regions, and streamlines workflows to reduce costs and accelerate speed-to-market. It also simplifies development by reducing boilerplate code and streamlining query management. Learn more.

Customer Success & Case Studies

Can you share specific case studies or success stories of customers using Hygraph?

Yes. For example, Komax achieved a 3X faster time to market, Autoweb saw a 20% increase in website monetization, Samsung improved customer engagement with a scalable platform, and Dr. Oetker enhanced their digital experience using MACH architecture. More stories are available here.

What industries are represented in Hygraph's case studies?

Industries include food and beverage, consumer electronics, automotive, healthcare, travel and hospitality, media and publishing, eCommerce, SaaS, marketplace, education technology, and wellness and fitness. See all case studies.

Who are some of Hygraph's customers?

Notable customers include Sennheiser, Holidaycheck, Ancestry, Samsung, Dr. Oetker, Epic Games, Bandai Namco, Gamescom, Leo Vegas, and Clayton Homes. See more.

Support & Implementation

How long does it take to implement Hygraph and how easy is it to start?

Hygraph is designed for quick implementation. For example, Top Villas launched a new project in just 2 months from the initial touchpoint. The platform is praised for its ease of use and intuitive interface, allowing even non-technical users to get started rapidly. Learn more.

What customer service or support is available after purchasing Hygraph?

Hygraph offers 24/7 support via chat, email, and phone. Enterprise customers receive dedicated onboarding and expert guidance. All users have access to detailed documentation, video tutorials, and a community Slack channel. Contact support.

What training and technical support is available to help customers get started?

Hygraph provides onboarding sessions for enterprise customers, 24/7 support, training resources such as video tutorials, documentation, webinars, and access to Customer Success Managers for expert guidance. Learn more.

KPIs & Metrics

What KPIs and metrics are associated with the pain points Hygraph solves?

Key metrics include time saved on content updates, system uptime, consistency in content across regions, user satisfaction scores, reduction in operational costs, time to market, maintenance costs, scalability metrics, and performance during peak usage. For more details, visit the CMS KPIs blog.

Blog & Community

Where can I find the Hygraph blog and developer tutorials?

The Hygraph Blog provides the latest updates, developer tutorials, and essential guides to content modeling. Visit the Blog section for more information.

How can I share or promote my timeline project built with Hygraph and SvelteKit?

You can share your project on social media using the provided Twitter link or LinkedIn link.

Velocity at Scale: Join the Launch of Hygraph’s Latest AI Innovations

Build a Personal Timeline with Hygraph and SvelteKit

Build and deploy your own personal timeline inspired by Polywork, with Hygraph and SvelteKit
Scott Spence

Written by Scott 

Jul 26, 2021
Building a Personal Timeline with Hygraph and SvelteKit

In this guide, I'll be making a personal timeline using Hygraph and SvelteKit, heavily inspired by Polywork’s approach to building personal profiles. If you’re wondering what Polywork is, it's a new platform where you can create a whole timeline of all your milestones and achievements and not just the roles or positions you have worked in before.

Here's what the end result will look like:

Polywork Clone with Hygraph

This option will give you that timeline you can add to your own site or have it as a stand-alone webpage.

This guide will cover getting your backend set up in Hygraph and creating your own content model of your timeline. I'll then go into displaying that content model on a web page.

If you've not used Hygraph before, you can take a look at the video playlists for how to Get Started with Hygraph and for further details, there's also the How to Hygraph playlist to check out.

#Create the content model

Taking a look at a user's Polywork profile (timeline) I'll break that down into the relevant content types.

So a high-level overview of the content types I'm going to need will be:

  • User: name, current position, location, bio, and pronoun.
  • User Badges: what your interests are, JavaScript, Rock Climbing, etc.
  • Activity Detail: this is the detail of what you did, like 'started a side project'.
  • Tags: These are the tags you've applied to your activity, like 'Published a YouTube video'.

I'll now break down the content model as I'm going to create it in Hygraph. All of the Hygraph features I'll be using can be done with the community plan in this example.

  • Model Name: Timeline User

    • Field types
      • Name > Single line text
      • Username > Single line text > Validations > Set fields as unique
      • Job title > Single line text
      • Company > Single line text
      • Pronoun > Single line text
      • Location > Single line text
      • Avatar > Asset picker
      • Banner > Asset picker
      • Bio > Multi line text

Timeline User Model.png

  • Model Name: User Badge

    • Field types
      • Name > Single line text
      • Icon > Asset

User Badge Model.png

  • Model Name: Activity Detail

    • Field types
      • Date > Date
      • Description > Rich text

Activity Detail Model.png

  • Model Name: Activity Tag

    • Field types
      • Name > Single line text
      • Badge Colour > Color

Activity Tag Model.png

Finally, for the content model, I will add some additional fields for the assets to include an alt text and a caption, Alt text as a 'Single line text' and the Caption as a 'Multi line text'.

Asset Model.png

Now that I have the models created, I can add in the relations for them with a Reference field:

  • Timeline User

    • User Badge > Model to reference > User badge > Allow multiple UserBadges per TimelineUser
    • Activity Details > Model to reference > Activity Detail > Allow multiple ActivityDetails per TimelineUser

User Badge Reference.png

If there were more than one user in the content model, I'd also check to Allow multiple Users per UserBadge and the same for the ActivityDetail. As it's only for me I'll keep it one to many for both.

Now there's a reverse field for Timeline User in the User Badge model and also for the Activity Detail.

  • User Badge

    • TimelineUser > Reference

User Badge with Reference.png

  • Activity Detail

    • Timeline User > Reference
    • Activity Tag > Model to reference > Activity Tag > Allow multiple ActivityDetails per ActivityTag + Allow multiple ActivityTags per ActivityDetail

Activity Detail Relation to Activity Tag.png

  • Activity Tag
    • Activity Details > Reference

If you're following along feel free to add your own validations to the fields, as it's only going to be small amounts of information I'm adding I'm going to leave them with the default validation.

Now that I have the content model created, I'll need to enable public access for the front end to use it.

In Settings > API access > Public Content API I'll enable the defaults by clicking the 'Yes, initialize defaults' button.

Hygraph content API permissions default.png

I'll copy some of the content from my Polywork profile and add it to my content model now.

#The front-end

For the front end, I'll be using SvelteKit, ultimately this can be any framework you are comfortable with. For the ease of scaffolding out the project, I'll be using SvelteKit along with DaisyUI which utilizes Tailwind and Tailwind prose.

As this is a single-page project and the main part of the project is on a single file, this can be done in any technology of your choosing.

I'll initialize the project with the following:

npm init svelte@next timeline

In my case I'm going to be choosing the following CLI options:

  • Skeleton project
  • No to TypeScript
  • No to ESLint
  • Yes to Prettier

Now if I pop open my text editor (VSCode), this is the basic project structure I have:

src/
│ └─ routes/
│ │ └─ index.svelte
│ └─ app.html
├─ static/
├─ .gitignore
├─ .npmrc
├─ .prettierignore
├─ .prettierrc
├─ jsconfig.json
├─ package.json
├─ README.md
└─ svelte.config.js

Time to install the dependencies, Tailwind and DaisyUI, along with Tailwind typography for great typography defaults it offers. Those along with GraphQL request, GraphQL.js (graphql) and Date FNS:

# add additional dependencies
npx svelte-add tailwindcss --jit
# 👆 this will configure Tailwind with Just In Time
npm i -D daisyui @tailwindcss/typography graphql-request graphql date-fns
npm i # shorthand for npm install

The Tailwind JIT compiler is optional, if you don't want to use it then you can remove the --jit from the npx command.

If you're following along, you'll notice svelte-add for Tailwind added some extra files to the project and changed others:

src/
│ └─ routes/
│ │ └─ __layout.svelte
│ └─ app.postcss
├─ postcss.config.js
├─ svelte.config.js
├─ tailwind.config.cjs
...rest mainly unchanged

Note the __layout.svelte was added and some additional config files for post CSS and Tailwind.

Popping open the __layout.svelte file I can see that the global CSS file has been added for the whole project:

<script context="module">
import '../app.postcss'
</script>
<main>
<slot />
</main>

I'm going to leave this unchanged and focus on the routes/index.svelte file for the majority of the work.

I'll need to add the typography plugin and DaisyUI to the tailwind.config.js file:

const config = {
mode: 'jit',
purge: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/typography'), require('daisyui')],
}
module.exports = config

There's also the default theme I'll need to set for DaisyUI in the srcapp.html file:

<!DOCTYPE html>
<html lang="en" data-theme="corporate">
<head>
<!-- rest of the file unchanged -->
</head>
</html>

Note that DaisyUI is after the typography plugin on the plugins array.

#Let's get to work!

As I mentioned earlier this will be a single file project, if you're following along and want to break it into more manageable parts, I'll leave that to you.

#Import the data for use on the page

Now, I need to get the data from the Hygraph endpoint, as it's a public endpoint I can put it straight into the code, but I'll add this to an environment file so that it can be used from one place. I'll create a .env file and add in the endpoint there:

touch .env

In the newly created .env file, I'll create an environment variable and assign the project endpoint to it:

VITE_CONTENT_API=https://api-eu-central-1.hygraph.com/v2/projectid/master

Not the VITE_ prefix is needed for Svelte to access the variable, Vite is the SvelteKit build tool and the VITE_ prefix is needed so the variable can be accessed on the client (browser).

Because I don't want to commit the .env file to GitHub I'll add .env to the .gitignore file, here's what my git ignore looks like:

.DS_Store
node_modules
/.svelte-kit
/package
.env

Now that I have the endpoint available to me via Vite, I can go about getting the data to use in the project.

I'll add a <script context="module"> to the top of my index.svelte file, this will fetch the data before the page loads.

In that block, I'm going to define a new GraphQLClient as hygraph then I'll bring in the API endpoint to use in the client.

I'll need to define a query to retrieve all the timeline information to use. I'll come onto that next for now. I'll define an empty gql tag to be passed to the client. The results from that query are then returned as props for the client to use.

<script context="module">
import { gql, GraphQLClient } from 'graphql-request'
export async function load() {
const hygraph = new GraphQLClient(
import.meta.env.VITE_CONTENT_API,
{
headers: {},
}
)
const query = gql`
`
const { timelineUser } = await hygraph.request(query)
return {
props: {
timelineUser,
},
}
}
</script>

Now, I need to define the query needed to pull all the user and timeline information.

In the Hygraph API playground I'll define all the fields I need:

{
timelineUser(where: { username: "spences10" }) {
id
name
username
jobTitle
company
pronoun
location
bio
avatar {
url
altText
}
banner {
url
altText
}
userBadges {
name
}
activityDetails(orderBy: createdAt_DESC) {
date
description {
html
}
activityTags {
id
name
badgeColour {
hex
}
}
}
}
}

That query can go into the empty gql tag I had in the previous code example. Note that I have hardcoded the timeline user with my username timelineUser(where: { username: "spences10" }), if you have more than one user then this would be a good time to look into SvelteKit routing. I won't be covering that in this post but you can check out the documentation if you want to take it to the next level with this project.

Sweet! So now that I have the data available to me to use in the client, I can now focus on displaying that data.

#Use the data on the page

I'll add a <script> directly under the closing <script context="module"> tag. This is where I can access the props returned by the <script context="module"> block.

I'll define export let timelineUser prop then destructure all the variables I'll need from it, and notice all the variables match up to the GraphQL query.

<script>
export let timelineUser
let { name, username, jobTitle, company, pronoun, location, bio, banner, avatar, userBadges, activityDetails } = timelineUser
</script>

Now I'll go about first adding in the banner, this will be at the top of the page taking up the full width of the page:

<img alt="{banner.altText}" src="{banner.url}" class="h-96 w-full" />

Now I'll add in some containers for the basics card (username, job title etc...) and the activity list, then create a component for those to be used in the index.svelte file:

<!-- Containers -->
<article class="mx-auto -mt-72 max-w-7xl px-4 relative sm:px-6 lg:px-8">
<div class="mx-auto max-w-3xl">
<!-- Basics Card -->
<div class="divider opacity-10 mb-10" />
<!-- Activities List -->
</div>
</article>

#Basics card

I'll create a new folder and files for the basics card and the activities components now in the terminal:

mkdir src/lib
touch src/lib/basics.svelte
touch src/lib/activities.svelte

In the basics.svelte file I'm going to need to import some props, I'll start with the avatar and the name. For this, I'll add the props inside some <script> tags and scaffold out the markup for the avatar and the name:

<script>
export let avatar
export let name
</script>
<div class="bg-white border my-16 card shadow">
<figure class="px-10 pt-10">
<div class="avatar">
<div class="rounded-full h-32 mb-8 w-32">
<img alt="{avatar.altText}" src="{avatar.url}" />
</div>
</div>
<h2 class="font-bold text-left leading-relaxed text-2xl">{name}</h2>
</figure>
</div>

Then, I can add in the following for username, jobTitle, company , pronoun and location to an unordered list:

<script>
export let avatar
export let name
export let username
export let jobTitle
export let company
export let pronoun
export let location
</script>
<div class="bg-white border my-16 card shadow">
<figure class="px-10 pt-10">
<div class="avatar">
<div class="rounded-full h-32 mb-8 w-32">
<img alt="{avatar.altText}" src="{avatar.url}" />
</div>
</div>
<h2 class="font-bold text-left leading-relaxed text-2xl">{name}</h2>
<ul class="flex space-x-2 text-left leading-relaxed opacity-75">
<li>@{username}</li>
<span class="opacity-75">&bull;</span>
<li>{jobTitle},</li>
<li>{company}</li>
<span class="opacity-75">&bull;</span>
<li>{pronoun}</li>
<span class="opacity-75">&bull;</span>
<li>{location}</li>
</ul>
</figure>
</div>

Next up for the basics card is adding in any user badges, I'll use the Svelte 'if' directive {#if userBadges} to check if there are any badges to render then looping over each badge in the userBadges props if there is with the Svelte 'each' directive {#each userBadges as { name }}:

<script>
export let avatar
export let name
export let username
export let jobTitle
export let company
export let pronoun
export let location
export let userBadges
</script>
<div class="bg-white border my-16 card shadow">
<figure class="px-10 pt-10">
<div class="avatar">
<div class="rounded-full h-32 mb-8 w-32">
<img alt="{avatar.altText}" src="{avatar.url}" />
</div>
</div>
<h2 class="font-bold text-left leading-relaxed text-2xl">{name}</h2>
<ul class="flex space-x-2 text-left leading-relaxed opacity-75">
<li>@{username}</li>
<span class="opacity-75">&bull;</span>
<li>{jobTitle},</li>
<li>{company}</li>
<span class="opacity-75">&bull;</span>
<li>{pronoun}</li>
<span class="opacity-75">&bull;</span>
<li>{location}</li>
</ul>
<div>
{#if userBadges}
<div class="flex flex-wrap mt-5 break-words relative">
{#each userBadges as { name }}
<div class="border rounded-full font-medium mr-2 mb-2 py-2 px-4">{name}</div>
{/each}
</div>
{/if}
</div>
</figure>
</div>

Last up is adding in the Multi line text for the bio, note I've added in some JavaScript to take care of any line breaks with {@html bio.split('\n').join('<br />')}, the @html is to render HTML directly if this is left out it will show the HTML tags in the markup:

<script>
export let avatar
export let name
export let username
export let jobTitle
export let company
export let pronoun
export let location
export let userBadges
export let bio
</script>
<div class="bg-white border my-16 card shadow">
<figure class="px-10 pt-10">
<div class="avatar">
<div class="rounded-full h-32 mb-8 w-32">
<img alt="{avatar.altText}" src="{avatar.url}" />
</div>
</div>
<h2 class="font-bold text-left leading-relaxed text-2xl">{name}</h2>
<ul class="flex space-x-2 text-left leading-relaxed opacity-75">
<li>@{username}</li>
<span class="opacity-75">&bull;</span>
<li>{jobTitle},</li>
<li>{company}</li>
<span class="opacity-75">&bull;</span>
<li>{pronoun}</li>
<span class="opacity-75">&bull;</span>
<li>{location}</li>
</ul>
<div>
{#if userBadges}
<div class="flex flex-wrap mt-5 break-words relative">
{#each userBadges as { name }}
<div class="border rounded-full font-medium mr-2 mb-2 py-2 px-4">{name}</div>
{/each}
</div>
{/if}
</div>
<div class="my-5 opacity-75">
<p>{@html bio.split('\n').join('<br />')}</p>
</div>
</figure>
</div>

Now, I can go over to the index.svelte file and import the basics card at the top of the file:

<script context="module">
import Basics from '$lib/basics.svelte'

Then to use the basics card and pass it all the props it needs:

<!-- Containers -->
<article
class="mx-auto -mt-72 max-w-7xl px-4 relative sm:px-6 lg:px-8"
>
<div class="mx-auto max-w-3xl">
<!-- Basics Card -->
<Basics
{avatar}
{name}
{bio}
{username}
{jobTitle}
{company}
{pronoun}
{location}
{userBadges}
/>
<div class="divider opacity-10 mb-10" />
<!-- Activities List -->
</div>
</article>

In Svelte, if the name of the prop being passed is the same as what's being expected by the component then there's no need to define the name of the prop, so avatar={avatar} becomes {avatar} neat eh?

Here's what I have so far:

Basics Card with Props.png

#Activity Details card

Now to go through the same process for the activities card, I've already got the file so in src/lib/activities.svelte I'll start scaffolding out what's needed there, this one is a bit simpler as it's only taking the activityDetails prop:

<script>
import { format } from 'date-fns'
export let activityDetails
</script>
<section>
{#if activityDetails}
{#each activityDetails as activity}
<section class="border mb-4 card shadow">
<div class="card-body">
{#if activity.activityTags}
{#each activity.activityTags as { name, badgeColour }}
<div class="flex flex-wrap break-words relative">
<div
class="border font-semibold mr-2 text-sm mb-2 w-auto py-2 px-4"
>
<p style="color:{badgeColour.hex}">{name}</p>
</div>
</div>
{/each}
{/if}
<p class="font-semibold mb-5 opacity-75">
{format(new Date(activity.date), 'do MMM yyy')}
</p>
<div class="prose">
{@html activity.description.html}
</div>
</div>
</section>
{/each}
{/if}
</section>

Again, there's a lot of use of the Svelte directives for 'if' and 'each' here along with the @html tag. I'll break it down a bit into what's going on here. The first 'if' is to conditionally render any activities if there are any and the next is to check if there are any activity tags applied to the activity.

If there are tags, they are being looped over to add to the activity. I'm using date-fns to format the date as I like then finally rendering out the activity detail!

Now, I can import this component into my index.svelte file at the top of the file like with the basics card:

<script context="module">
import Activities from '$lib/activities.svelte'
import Basics from '$lib/basics.svelte'

And that's it! 😅

If you want to check out the code, I've made it available on GitHub.

#Deploy

I'll be deploying this project to Vercel. There are other providers available like Render and Netlify if you prefer, but for the ease of setting up, I'll be using Vercel.

I'll need to install a SvelteKit adapter for Vercel, I'll install it with the following command:

npm i -D @sveltejs/adapter-vercel@next

Note the @next as part of the install.

Next up, I'll need to add the adapter to my svelte.config.js file, here's what mine looks like:

import adapter from '@sveltejs/adapter-vercel'
import preprocess from 'svelte-preprocess'
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
adapter: adapter(),
},
preprocess: [
preprocess({
postcss: true,
}),
],
}
export default config
// Workaround until SvelteKit uses Vite 2.3.8 (and it's confirmed to fix the Tailwind JIT problem)
const mode = process.env.NODE_ENV
const dev = mode === 'development'
process.env.TAILWIND_MODE = dev ? 'watch' : 'build'

From here, I can use the Vercel CLI and use vc from the command line to deploy it. I have also the added option to link the Vercel project to a GitHub repo.

If you're using SvelteKit and want to use a different hosting provider there are several adapters available via the SvelteKit documentation.

That's it, thanks for reading, if you want to take a look at the code for this you can find it over on my GitHub and the working example can be found here.

Blog Author

Scott Spence

Scott Spence

Developer Advocate @Hygraph • Learner of things • Jamstack • Svelte

Share with others

Sign up for our newsletter!

Be the first to know about releases and industry news and insights.