builder
builder copied to clipboard
Vue/Nuxt Documentation is confusing and incomplete
@samijaber @steve8708 PLEASE HELP 🙏
I recently opened a support ticket via email but that did not result in sufficient resolution or a way forward. Is there any update on the stability of the SDK for Vue3/Nuxt3? This doesn't seem to work as expected. However I can't get a baseline because the Page model example is the only thing that worked as expected when following guides on https://www.builder.io/c/docs or https://www.builder.io/c/blueprints
There are a number of discrepancies between the examples that are spread all over the documentation and it is difficult to figure out what is most current. Here are some examples of conflicting information...
Examples in the Builder Editor for Vue/Nuxt
Nuxt seems to demonstrate using the older Vue2 sdk
While Vue shows both Vue2 and Vue3 but doesn't provide context
Examples for Section Type models following Announcement bar guide with Vue/Nuxt
- does not mention of API Key (as above)
- if API_KEY is part of runtime configs in nuxt.config.js
- or if it needs to be on the Content component as props (also is this the same as RenderContent as mention above???)
Nuxt3 general user exceptions are that a Nuxt Module uses configs in runtime for API-KEYs or app public configs for public keys which goes in the nuxt.config.js. This doesn't mention either method or where to init the module for usage with Section type only renderings.
Editorial Example
This one shows both fetchOneEntry as well as using an API key on the component
- :thinking: Not sure why component needs the API key if we need to make async key call with fetch function.
- There is no context or explanation so very difficult to understand
Main Questions:
- Do we use builder.init() or is that the old vue SDK and we should use @builder.io/sdk-vue instead and set an api-key prop on the component?
- If using component props is the correct method to init, why do we need a fetch function to call the data (that also has api-key as params? Is that not what the Content is doing with the api-key prop?
- Is there really a helper method getContent? Or is it fetchOneEntry? Or do we just use Fetch API and a rest endpoint?
I love what this project is aiming to do and because of its power I would like to use this with Vue/Nuxt often. 😍 But these example aren't concrete enough to understand and its a blocker to getting started.
I know there is a lot of busy work going on so I mentioned to Support and Partner Program reps that I would be willing to help clean up the docs and contribute back if I can get someone to explain the appropriate usage. Either way is it possible to close the gaps for Vue3 and Nuxt3 framework guides so they agree site-wide across the documentation and all examples? I really want to get using this but feeling frustrated at every step on-boarding myself.
Thanks :+1:
@jbool24 Thanks for filing as a separate issue.
To answer your question: our Vue SDK is perfectly stable and reliable on Vue 3 (with or without Nuxt 3). It has hit v1.0 recently and we continue to improve it.
Thank you for pointing out some of the inconsistencies in our docs. They were relying on old syntax from our first generation Vue SDK which is no longer supported. We are actively going over our Vue documentation now to make sure all old examples are updated to match the Gen 2 Vue SDK.
Meanwhile, I encourage you to look at our example codebases which are up to date. You can see links to all of them here https://github.com/BuilderIO/builder/tree/main/packages/sdks/output/vue#usage
To answer your questions: builder.init() is the old way. Instead, you must use fetchOneEntry and provide the api-key prop to the component. The latter is needed for Symbols to dynamically fetch their content, which is sometimes needed.
Thanks so much @samijaber!! :pray: :heart: I appreciate you looking into this.
Is it safe to assume the new doc updates will reflect more context about what [symbols need] to dynamically fetch their content is about? That sounds confusing still if the fetchOne is getting the content or is that an internal implementation that I should be digging into source code for? Or something lib users don't need to understand?
If it's the latter can I make a suggestion to allow environment variables be introduced to hide/remove this detail from the view of the user? It feels strange to have to supply it on the component.
Meanwhile, I encourage you to look at our example codebases which are up to date. You can see links to all of them here https://github.com/BuilderIO/builder/tree/main/packages/sdks/output/vue#usage
Thanks for directing me here! Didn't even know that existed. I was lookin' :point_right: https://github.com/BuilderIO/builder/blob/main/examples/vue/nuxt-3 :point_right: and here https://github.com/BuilderIO/builder/tree/main/examples/vue/vue-3 and they only have Page model examples.
Thank for this support.
You do not need to worry about why the api-key is needed, or when it is used. THe SDK takes care of that.
We do have internal tickets tracking an issue on how to better explain the API key prop in <Content>.
For your information:
When fetching content with deeply nested symbols, it is possible that the nested symbol's content is not recursively fetched in the fetchOneContent (depending on whether you set the enrich flag to true or false, among other things). This is why we require the api-key prop to Content. If we encounter a scenario where the Symbol's content was not provided in the original JSON, we must perform additional requests to acquire the content.
Note that the API key is public: it is not a private key, and is therefore safe to consume and store on the client side.
Thanks for clarification. I would still like to recommend that for use cases in the Nuxt community, using runtimeConfigs values for keys (public or not) is the recommended way to manage them. Also, if loading Builderio recommends using a module llike @builder.io/sdk-vue/nuxt like what is currently recommended to do hereLanding Pages Then a more Nuxt aligned uasge is to add keys to the module configs to allow the module to configure itself at build time via the internal module.ts and runtime scripts (such as plugins which would handle prop default options). These things get injected into the client code as well.
For full clarify here is an example of what the module and runtime plugin could do
// <module-root>/module.ts
import {
defineNuxtModule,
addPlugin,
addComponentsDir,
addImports,
addImportsDir,
createResolver
} from "@nuxt/kit";
import type { NuxtHookName } from "@nuxt/schema";
export interface ModuleOptions {
publicApiKey: string,
devtools: boolean, // enable nuxt/devtools integration
apiOptions: any, // default options for fetchOneEntry
customComponentsDir: string, // enable global directory for custom builder components
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: "@builder.io/sdk-vue/nuxt",
configKey: "builderio"
},
defaults: {
publicApiKey: '',
devtools: false,
customComponentsDir: '~/components/builderio',
apiOptions: {},
},
setup(options, nuxt) {
const resolver = createResolver(import.meta.url);
if(nuxt.options.vite.optimizeDeps) {
nuxt.options.vite.optimizeDeps.include = nuxt.options.vite.optimizeDeps.include || [];
// anything to manually add to the bundle
nuxt.options.vite.optimizeDeps.include.push("@builder.io/sdk-vue");
nuxt.options.vite.optimizeDeps.exclude = nuxt.options.vite.optimizeDeps.exclude || [];
// anything to exclude from the bundle
nuxt.options.vite.optimizeDeps.include.push("@builder.io/sdk-vue");
}
// Enable dirs
if(options.customComponentsDir) {
addComponentsDir({ path: options.customComponentsDir, global: true, pathPrefix: false });
}
nuxt.options.build.transpile.push(resolver.resolve("./runtime"));
nuxt.options.build.transpile.push("@builder.io/sdk-vue"); // <-- if we need/want to transpile
// Add plugin
nuxt.options.runtimeConfig.public.builderio = options;
const enablePluginCondition = options.usePlugin === true && options.enableSudoMode === false;
// here's magic sauce 🪄 to pre-config the key a bundle time.
// User doesn't need to care after setting ENV or directly in nuxt.config.ts
if (enablePluginCondition) {
addPlugin(resolver.resolve("./runtime/plugin")); // <-- see ./runtime/plugin.ts example below
}
// Add global CSS
nuxt.options.css.push('@builder.io/sdk-vue/css');
// Add auto imports globally
const names = [
"fetchEntries",
"fetchOneEntry",
];
for (const name of names) {
addImports({ name, as: name, from: "@builder.io/sdk-vue" });
}
// Add composables if there are helpers
addImportsDir(resolver.resolve("./runtime/composables"));
// We could maybe embed the live Editor too 🤷 if thats possible
if (options.devtools) {
nuxt.hook('devtools:customTabs' as NuxtHookName, (iframeTabs: Array<unknown>): void => {
iframeTabs.push({
name: 'builderio',
title: 'Builder.io',
icon: 'i-logos-builderio-icon',
view: {
type: 'iframe',
src: 'https://builder.io/content',
}
})
})
}
}
});
// <module-root>/runtime/plugin.ts
import { Content as BuilderContent } from '@builder.io/sdk-vue';
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
/**
* Builder.io Pre-configuration from
*
* This would be set in nuxt.config.ts
* builderio {
* publicApiKey: string - could also be set by user with import.meta.env.NUXT_BUILDERIO_PUBLIC_API_KEY
* devtools: boolean
* apiOptions: any
* customComponentsDir: string
* }
*/
export default defineNuxtPlugin(({ vueApp }) => {
let { builderio } = useRuntimeConfig().public;
builderio = JSON.parse(JSON.stringify(builderio));
vueApp.use(BuilderContent, { ...builderio });
});
This would represent a much better DX for Nuxt users as this is what we are most accustomed to.
And with vanilla Vue thanks to Vite, its usually preferred to use import.meta.env for such values. At bundle time Vite will rewrite all ENV vars that are marked with VITE_ into the bundle. Nuxt also offers this method as well as another alternative to runtimeConfigs in nuxt.config.ts. Anything with NUXT_ is bundled to the client for browser-side use. This might be a better way to conceal the fact that API key is being used inside the component and one less prop for the user to be concerned about. Thanks again for this consideration. :+1: :hugs: