I am building a blog using Storyblok (v2) and Next.js (v13). I understand that NEXT_PUBLIC environment variables can be exposed to the browser, and I do not want my access token exposed. I have used the api folder in the past to accomplish this in component files, but I do not think that I can use that for the page files—unless I am wrong. I have read the documentation from Next.js, and I am confused as to how I can accomplish this in my _app.js file. I am still learning about how to properly hide these using Next.js, and I was successful in my previous attempt.
This is what I have tried so far after searching for answers. I have added my access token to a .env file in the root of my project. I did not use NEXT_PUBLIC. I left that off because in my other project where I was able to hide my environment variables from the browser, that is how I did it. I also added it to my next.config.js file based on the information that I found in my search. This is what that file looks like:
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
env: { ACCESS_TOKEN: process.env.ACCESS_TOKEN },
}
module.exports = nextConfig
My _app.js file looks like this:
import { storyblokInit, apiPlugin } from '#storyblok/react'
import '../styles/globals.css'
storyblokInit({
accessToken: process.env.ACCESS_TOKEN,
use: [apiPlugin],
});
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Of course the above does not work. When I add {process.env.ACCESS_TOKEN} to any page, it is showing the token. I understand why this does not work based on the docs that I read, but I just wanted to try everything that I could before posting my question here.
Prior to doing these things, I also tried adding my variable to the .env file in the root of my project (not using the NEXT_PUBLIC prefix), and then in my _app.js file, I had the following:
import { storyblokInit, apiPlugin } from '#storyblok/react'
import '../styles/globals.css'
storyblokInit({
accessToken: token,
use: [apiPlugin],
});
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
function getStaticProps() {
return {
props: {
token: props.env.ACCESS_TOKEN
}
}
}
My next.config.js file wasn't touched, so it did not contain env in it. The error that I get is that token is not defined. This article is where I found the steps that I attempted.
I started using trial and error after that—mainly because I went down the Google rabbit hole and got super frustrated. This was my Hail Mary:
import { storyblokInit, apiPlugin } from '#storyblok/react'
import '../styles/globals.css'
function MyApp({ Component, pageProps, props }) {
storyblokInit({
accessToken: props.token,
use: [apiPlugin],
});
return <Component {...pageProps} />
}
export default MyApp
function getStaticProps() {
return {
props: {
token: props.env.ACCESS_TOKEN
}
}
}
The error that I get here is: Cannot read properties of undefined (reading 'token')
Is what I am trying to do even possible in the _app.js file? I am happy to read any other documentation that I may not have found in my search if that is a better response to my question.
Related
I am trying to set up a graphql frontend application with nextjs13 but with the new folder structure as there ins't _app.tsx I am wondering how to setup
Before we use to wrap the entire app like this
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
Now how to set up that?
What you want to do now is leverage the layout functionnality of the new nextjs 13 routing.
Your _app.js now becomes the root layout, a layout.js file that must be placed at the base of the app directory.
As any file in this directory, they will be treated as server component by default, which will prevent your Provider to work. Therefore, you need to specify "use client" in this file and feed it the provider that will then be shared by the whole application.
Example code below:
"use client";
import { ApolloProvider } from "#apollo/client";
type RootLayoutProps = {
children: React.ReactNode;
};
const RootLayout = ({ children }: RootLayoutProps) => {
return (
<html>
<body>
<ApolloProvider>{children}</ApolloProvider>
</body>
</html>
);
};
export default RootLayout
The accepted answer doesn't work when I have attempted it like that. As mentioned in the comments this:
TypeError: Cannot read properties of undefined (reading 'Symbol(APOLLO_CONTEXT)')
Is the resulting error.
I found doing this works:
// /apollo/ApolloProvider.js
"use client";
import { ApolloProvider as Provider } from "#apollo/client";
import { client } from "./client";
export const ApolloProvider = ({ children }) => (
<Provider client={client}>{children}</Provider>
);
// /app/Layout.js
import { ApolloProvider } from "apollo";
const RootLayout = ({ children }) => (
<ApolloProvider>{children}</ApolloProvider>
);
export default RootLayout;
I have been trying for ages to make Sanity work with React Native and It's not working out, read multiple documentations, followed many tutorials but no luck.... What am i doing wrong?
Made a simple recreation here:
https://snack.expo.dev/#spts/smelly-candies
Please let me know what I'm doing wrong, or what I have to do to make it work
There are a few things wrong here, first I'll assume you meant Sanity and not Strapi:
Data isn't loading from Sanity because you need to enable CORS for the expo playground: see more details here
Make sure you set an apiVersion in sanity.js
There were a few issues with your React code which I've updated below, and should work once the CORS issue is resolved.
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import sanityClient from './sanity.js'
export default function App() {
const [stuff, setStuff] = useState([]);
useEffect(() => {
async function fetchData() {
const query = '*[_type == "post"] { title, content, emojiType }';
const data = await sanityClient.fetch(query);
setStuff(data)
}
fetchData()
}, [])
return (
<View>
<Text >
Change code in the editor and watch it change on your phone! Save to get a shareable url.
</Text>
<Text>{JSON.stringify(stuff)}</Text>
</View>
);
}
I've searched Stack Overflow for similar questions but the answers either refer to old versions or aren't relevant to my situation.
I get the above error for the first component on the page which uses Styled Components. If I remove that component I get the error for the next one and so on, so I assume there's a problem with the hydration process and it's happening for every className.
I have a _document.tsx file which extends the Document class and has the following getInitialProps function
static async getInitialProps (ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
}
} finally {
sheet.seal()
}
}
My hunch is that it is something to do with this code because before I upgraded to Typescript (and had to change my _app.tsx file) I didn't get this error, but I have no idea how to fix it.
Any help would really be appreciated.
Try installing babel-plugin-styled-components, then add a .babelrc file to the root of the project with the following configuration. This ensures that no mismatches in classes generation occur during rehydration.
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
From the babel-plugin-styled-components's documentation:
By adding a unique identifier to every styled component, this plugin avoids checksum mismatches due to different class generation on the client and on the server. If you do not use this plugin and try to server-side render styled-components React will complain with an HTML attribute mismatch warning during rehydration.
Next.js added support for Styled Components since v12.0.1. The only thing you need to do is modify the next.config.js file. It should look like the following.
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// This is the property you need to add
compiler: {
// ssr and displayName are configured by default
styledComponents: true,
},
};
module.exports = nextConfig;
Source: Next.js Docs
So, in the docs, we have this code:
https://react-query.tanstack.com/guides/ssr#using-hydration
RE: The use of 'useRef' to store a reference.
// _app.jsx
import { QueryClient, QueryClientProvider } from 'react-query'
import { Hydrate } from 'react-query/hydration'
export default function MyApp({ Component, pageProps }) {
const queryClientRef = React.useRef()
if (!queryClientRef.current) {
queryClientRef.current = new QueryClient()
}
return (
<QueryClientProvider client={queryClientRef.current}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
BUT, I also need to store some fetch calls IN the "cache" in the MyApp.getInitialProps.... how is that gonna happen IF I create an instance with useRef in function above? Meaning, how is my "getInitialProps" gonna get that instance?
MyApp.getInitialProps = async (appContext) => {
// in here, I do a fetch and get some data I need for SSR
// user Session etc...
const { user } = await fetchUserSession();
// WHAT INSTANCE IS THIS?
queryClient.setQueryData('user', user || {});
return {
...appProps,
dehydratedState: dehydrate(queryClient),
}
}
I am currently defining queryClient = new QueryClient() at the top of the page, so "both" can use it. But I think that is causing some issues with hydration when I npm run build this app.
Remember, this is in "_app.js" so I have to use getInitialProps.
The reason I am doing it here is because we need the users session sitewide, no matter what page they and on. So, rather than do this in every single /page/, just do it in _app.js, so the whole site needs that? The /page/ are Static Generated.
for prefetching on the server, you just create a new QueryClient like described further down on the page you have linked:
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery('posts', getPosts)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
Here, you create a new empty client, prefetch and take the state and dehydrate it. Then, on the frontend, that state is put into your instance client from MyApp. This is just a way of getting the data from that server-side cache into the client-side cache.
I'm using nextjs 9.3.5 and even the simplest example of getServerSideProps is always failing:
function Page({ data })
{
//Prints undefined
console.log(data);
return <div>Data in props: {data}</div>
}
export async function getServerSideProps()
{
var data = "Hello";
//Prints "Hello"
console.log(data);
return { props: { data } };
}
export default Page
This is basically a cut and paste from the very simple example on the nextjs website. getInitialProps works fine.
In case you added _app.js file into your project according official documentation you need add Component and pageProps inside, here is a minimal implementation of _app.js file.
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
I had the same problem and I realised that I was using getServerSideProps in the component that is not the actual page, rather it was my card component that I was displaying on the page.
This should be in the structure of your json that you are returning as a prop.
Try this:
export async function getServerSideProps() {
const data = {title:"Hello Sir"};
return { props: data }
}