Vue 3 Integration
Vue 3 Integration
Import the Vue adapter from @rankhiker/sdk/vue (Vue 3 only).
Browser baseUrl note
Vue components run in the browser, so pin the www host to keep the API key header from being dropped across the apex-to-www redirect:
{ apiKey: '...', baseUrl: 'https://rankhiker.com' }
Plugin (app level)
Register once with app.use, passing either a client or a config:
import { createApp } from 'vue';
import { rankHikerPlugin } from '@rankhiker/sdk/vue';
import App from './App.vue';
createApp(App)
.use(rankHikerPlugin, {
config: {
apiKey: import.meta.env.VITE_RANKHIKER_API_KEY,
baseUrl: 'https://rankhiker.com',
},
})
.mount('#app');
provideRankHiker (component level)
Call inside setup() to provide a client to descendants. Pass a pre-built RankHikerClient or a config object:
import { provideRankHiker } from '@rankhiker/sdk/vue';
provideRankHiker({
apiKey: import.meta.env.VITE_RANKHIKER_API_KEY,
baseUrl: 'https://rankhiker.com',
});
useRankHikerClient
Returns the provided client. Throws if neither the plugin nor provideRankHiker supplied one above the component.
import { useRankHikerClient } from '@rankhiker/sdk/vue';
const client = useRankHikerClient();
useArticles
Returns reactive refs { data, loading, error, refetch }.
The params argument may be a plain object (treated as static), or a ref, computed, or getter for reactivity. To refetch when a value changes, pass a getter:
import { ref } from 'vue';
import { useArticles } from '@rankhiker/sdk/vue';
const workspaceId = ref('665abc...');
// Getter form: refetches whenever workspaceId changes
const { data, loading, error, refetch } = useArticles(() => ({
workspaceId: workspaceId.value,
limit: 10,
}));
useArticle
Returns reactive refs { data, loading, error }. The slug may be a ref or getter, and the fetch is skipped while the slug is nullish:
import { useArticle } from '@rankhiker/sdk/vue';
const { data, loading, error } = useArticle(() => route.params.slug as string);
BlogList
An unstyled list of articles. Props:
| Prop | Type | Notes |
|---|---|---|
params | ListArticlesParams | Filter params for the API call. |
class | string | Class on the grid container. |
cardClass | string | Class on each card. |
hrefForPost | (post) => string | Slug to URL mapping. Defaults to /blog/{slug}. |
card (scoped, receives { post }), loading, empty, and error (scoped, receives { error }).
<script setup lang="ts">
import { BlogList } from '@rankhiker/sdk/vue';
</script>
<template>
<BlogList :params="{ limit: 12 }" class="blog-grid" card-class="blog-card">
<template #card="{ post }">
<a :href="/blog/${post.slug}">
<h3>{{ post.title }}</h3>
<p>{{ post.excerpt }}</p>
</a>
</template>
<template #empty>
<p>No posts yet.</p>
</template>
<template #error="{ error }">
<p>{{ error.message }}</p>
</template>
</BlogList>
</template>
BlogPost
Renders a single article by slug. The body HTML is rendered via innerHTML. Props:
| Prop | Type | Notes |
|---|---|---|
slug | string | Required. Article slug. |
class | string | Class on the article wrapper. |
loading, notFound, error (scoped, { error }), and the default slot (scoped, { post }) to fully take over rendering.
<script setup lang="ts">
import { BlogPost } from '@rankhiker/sdk/vue';
defineProps<{ slug: string }>();
</script>
<template>
<BlogPost :slug="slug" class="prose">
<template #notFound>
<p>That post does not exist.</p>
</template>
</BlogPost>
</template>
Take over rendering with the default slot:
<BlogPost :slug="slug">
<template #default="{ post }">
<article>
<h1>{{ post.title }}</h1>
<p>{{ post.wordCount }} words</p>
<div v-html="post.content" />
</article>
</template>
</BlogPost>
A full SFC blog (list and detail)
<script setup lang="ts">
import { ref } from 'vue';
import { provideRankHiker, BlogList, BlogPost } from '@rankhiker/sdk/vue';
provideRankHiker({
apiKey: import.meta.env.VITE_RANKHIKER_API_KEY,
baseUrl: 'https://rankhiker.com',
});
const slug = ref<string | null>(null);
</script>
<template>
<BlogPost v-if="slug" :slug="slug">
<template #default="{ post }">
<button @click="slug = null">Back</button>
<h1>{{ post.title }}</h1>
<div v-html="post.content" />
</template>
</BlogPost>
<BlogList v-else :params="{ limit: 12 }">
<template #card="{ post }">
<button @click="slug = post.slug">{{ post.title }}</button>
</template>
</BlogList>
</template>
Was this article helpful?