How to correctly use page components without GraphQL - reactjs

I have a new Gatsby site, which has 3 pages (with more to come). I am finding that I am having to repeat lots of styling and I know there must be a correct way to avoid doing this but I'm unsure what. I'm using Emotion with Tailwind.
There is a hero element on all pages, which include a heading and description:
<Hero>
<Title>
Page title here
</Title>
<Lead>
Descirption text here
</Lead>
</Hero>
This is the styling for it:
const Hero = styled.header`
${tw`bg-blue-dark p-6`};
`
const Title = styled.h1`
${tw`text-white tracking-wide font-medium`};
`
const Lead = styled.p`
${tw`text-gray leading-relaxed mb-1`};
a {
${tw`text-white font-medium no-underline text-purple hover:text-white`};
}
`
Some pages also have action buttons:
<Actions>
<LinkPrimary to="/some-page/">Click for more</LinkPrimary>
<LinkSecondary to="/some-other-page/">Or click here</LinkSecondary>
</Actions>
Full page template looks like (this is what I'm duplicating for every new page):
import React from "react"
import Layout from "../components/layout"
import styled from "#emotion/styled"
import { Link } from "gatsby"
import tw from "tailwind.macro"
const Hero = styled.header`
${tw`bg-blue-dark p-6`};
`
const Title = styled.h1`
${tw`text-white tracking-wide font-medium`};
`
const Lead = styled.p`
${tw`text-gray leading-relaxed mb-1`};
a {
${tw`text-white font-medium no-underline text-purple hover:text-white`};
}
`
const Actions = styled.section`
${tw`text-center m-2 mt-8 mb-24`};
`
const LinkPrimary = styled(Link)`
${tw`block bg-pink hover:bg-green-light text-blue-dark font-bold no-underline py-4 px-4 m-4 rounded`}
`
const LinkSecondary = styled(Link)`
${tw`block bg-regal-blue hover:bg-blue-light text-pink hover:text-white font-semibold no-underline py-4 px-4 m-4 rounded`}
`
export default () => (
<Layout>
<Hero>
<Title>
Hey I'm The About Page
</Title>
<Lead>
Learn all about us
</Lead>
</Hero>
<Actions>
<LinkPrimary to="/some-page/">Click for more</LinkPrimary>
<LinkSecondary to="/some-other-page/">Or click here</LinkSecondary>
</Actions>
</Layout>
)
The problem I have is for every new page I have to repeat the styling. I am manually creating these pages within src/pages and editing the title and description for each page. Pages that have the buttons I am also editing the button text and URL.
I'm guessing there must be a way to create a "hero" component which includes the title and lead with their styling, then import it into each page and edit the content on a per-page basis.
Not all pages will have the action buttons so they probably need to be in their own component and just imported where needed.
If someone could give me a basic demo or link to some docs where this is explained that would be much appreciated. All my research only gives examples of how to do this querying with GraphQL.

Alright, I think I have something that works so will post an answer.
Create a Hero component src/components/hero.js
import React from "react"
import styled from "#emotion/styled"
import tw from "tailwind.macro"
const Header = styled.header`
${tw`p-6`};
`
const Title = styled.h1`
${tw`text-white tracking-wide text-lg font-medium`};
`
const Lead = styled.p`
${tw`text-purple-light leading-relaxed text-lg`};
`
const Hero = props => (
<Header>
<Title>{props.heading}</Title>
<Lead>{props.text}</Lead>
</Header>
)
export default Hero
Then use it in your pages, index page, about page, wherever it's needed:
import React from "react"
import Layout from "../components/layout"
import Hero from "../components/hero"
export default () => (
<Layout>
<Hero
heading="Hello world!"
text="This is the lead text for this page."
/>
...
</Layout>
)
The great thing about this is now my styling for my hero element is all contained in my Hero component. I will probably do the same for the call to action buttons.
If you want to have links or html tags in your text, you can do the following:
<Hero
heading={[<em>Links</em>, " that", <br />, " go places"]}
text={[
"You can write some text here followed by ",
<Link to="/learn/">
a link to another page
</Link>,
" that you can click.",
]}
/>

Related

Build fails because of styles.js [duplicate]

This question already has answers here:
Can't build React/Next project - found page without a React Component as default export (context api file)
(4 answers)
Closed 3 months ago.
I have this NextJS project that im trying to deploy. But when it builds, it fails an gives me this error:
Build optimization failed: found pages without a React Component as default export in pages/portfolio/styles pages/styles
Those files are these:
import tw from "tailwind-styled-components";
export const Wrapper = tw.div`
h-screen
flex
flex-col
`;
export const Main = tw.main`
`;
export const Container = tw.div`
flex
items-center
justify-center
flex-col
`;
export const Footer = tw.div`
w-full
flex
items-center
justify-center
min-h-[50px]
bg-black
mt-5
`;
export const HeroSection = tw.div`
relative
w-full
h-[70vh]
lg:h-[100vh]
bg-black
`;
export const HomeGallery = tw.div`
grid
grid-cols-1
w-full
`;
export const About = tw.div`
text-justify
text-lg
px-5
py-9
`;
And
import tw from "tailwind-styled-components";
export const Container = tw.div`
`;
export const ImagesContainer = tw.div`
grid
grid-cols-1
`;
I tried using styled components instead of taiwilnd styled components and got the same problem. I remember using styles.js files with styled components on another project so I don't understand where's the problem
I hope this can help you. I think it's nothing major problem in your work,isn't that you are not import React yet. You can find more references about how to use styled component at Medium in link :
Use Styled Component on Reactjs

Issues with Next.js/React forwardRef() function

While developing a website for a class (I used a youtube tutorial to build the site), I am having this error show up in the console:
Although the site renders locally, this causes issues when I try to deploy it, as you can imagine. So I found the documentation for the React.forwardRef() and I implemented it like this in my code:
import React from 'react';
import Image from "next/image";
import styles from "../styles/PizzaCard.module.css";
import Link from 'next/link';
const PizzaCard = React.forwardRef(({pizza}, ref) => {
return <input ref={ref}/>>(
<div className={styles.container}>
<Link href={`/product/${pizza._id}`} passHref>
<Image src={pizza.img} alt="" width="500" height="500"/>
</Link>
<h1 className={styles.title}>{pizza.title}</h1>
<span className={styles.price}>${pizza.prices[0]}</span>
<p className={styles.desc}>
{pizza.desc}
</p>
</div>
);
});
export default PizzaCard;
And this in my PizzaList file:
import React from "react";
import styles from "../styles/PizzaList.module.css";
import PizzaCard from "./PizzaCard";
const PizzaList = React.forwardRef(({pizzaList}, ref) => {
return <input ref={ref}/>> (
<div className = {styles.container}>
<h1 className={styles.title}>The Mellowist Pizza in Town!</h1>
<p className={styles.desc}>
Mellow Yellow Pizzaria is a local Family Owned business providing
the community with tasty pizza made with Heart and Soul!
</p>
<div className={styles.wrapper}>
{pizzaList.map((pizza) => (
<PizzaCard key={pizza._id} pizza={pizza} />
))}
</div>
</div>
)
})
export default PizzaList
And here is where PizzaList is called:
import axios from "axios";
import Head from "next/head";
import Image from "next/image";
import { useState } from "react";
import Add from "../components/Add";
import AddButton from "../components/AddButton";
import Featured from "../components/Featured";
import PizzaList from "../components/PizzaList";
import styles from "../styles/Home.module.css";
export default function Home({pizzaList, admin}) {
const [close, setClose] = useState(true)
return (
<div className={styles.container}>
<Head>
<title>Mellow Yellow Pizzaria</title>
<meta name="description" content="Created by Yellow project team at CTU" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Featured/>
{admin && <AddButton setClose={setClose}/>}
<PizzaList pizzaList={pizzaList} />
{!close && <Add setClose={setClose}/>}
</div>
)
}
export const getServerSideProps = async (ctx) =>{
const myCookie = ctx.req?.cookies || ""
let admin = false
if(myCookie.token === process.env.TOKEN){
admin = true
}
const res = await axios.get("http://localhost:3000/api/products")
return{
props:{
pizzaList:res.data,
admin,
}
}
}
Although this made the console errors go away, now my pizza products do not display, as you can see:
So, what am I doing wrong? If you need me to post more of my code, please let me know and I will, I'm not sure what all you'd need to see.
EDIT:
Here is my original code before adding the forwardRef()...this is the code that gives me the console errors in the first screenshot, I added the forwardRef() to PizzaCard and PizzaList because those are 2 spots that the console suggested I check (the list of "at ..." in the console window)
PizzaCard:
import React from 'react';
import Image from "next/image";
import styles from "../styles/PizzaCard.module.css";
import Link from 'next/link';
const PizzaCard = ({pizza}) => {
return (
<div className={styles.container}>
<Link href={`/product/${pizza._id}`} passHref>
<Image src={pizza.img} alt="" width="500" height="500"/>
</Link>
<h1 className={styles.title}>{pizza.title}</h1>
<span className={styles.price}>${pizza.prices[0]}</span>
<p className={styles.desc}>
{pizza.desc}
</p>
</div>
);
};
export default PizzaCard;
PizzaList:
import styles from "../styles/PizzaList.module.css";
import PizzaCard from "./PizzaCard";
const PizzaList = ({pizzaList}) => {
return (
<div className = {styles.container}>
<h1 className={styles.title}>The Mellowist Pizza in Town!</h1>
<p className={styles.desc}>
Mellow Yellow Pizzaria is a local Family Owned business providing
the community with tasty pizza made with Heart and Soul!
</p>
<div className={styles.wrapper}>
{pizzaList.map((pizza) => (
<PizzaCard key={pizza._id} pizza={pizza} />
))}
</div>
</div>
)
}
export default PizzaList
Here is a link to my github with all of the code:
https://github.com/InvisibleH3R0/mellowyellowpizzaria
Original Issue Fixed
So the fix for the ref issue was to wrap the image (in the first set of code I posted) in <a></a>
But now...when I load the site (locally) the homepage starts off just white, if I refresh the page it comes up...but when I inspect the page when first loading, it shows a internal server (500) error:
This leads me to believe the issue lies in the api/products or api/options code, that is where the GET methods are

Storybook cannot read property 'displayName' of undefined

I have created a new component Navbar.jsx
import { Disclosure } from '#headlessui/react'
import Image from 'next/image'
import tacoPicture from '../public/lets-taco-bout-it.png'
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export const Header = () => {
return (
<Disclosure as="nav" className="bg-white shadow">
{({ open }) => (
<>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<div className="flex-shrink-0">
<Image src={tacoPicture} alt="Picture of the author" />
</div>
</div>
</div>
</div>
</>
)}
</Disclosure>
)
}
So, this needs to be described as a story. In my file Navbar.stories.jsx I do the following
import { Navbar } from './Navbar';
export default {
title: 'Example/Navbar',
component: Navbar,
};
const Template = (args) => <Navbar {...args} />;
export const Default = Template.bind({});
And get the error:
I am new to storybook, however it seems to be a react issue, which I am also new to.
Faced the same issue. What is the mistake I have done is using the wrong import.
So i change this,
import { Button } from './button';
Into this
import Button from './button';
I got the idea from #Katharina's answer. thanks...
Apparently, I was importing a Navbar, my component's name is Header. Also there is a difference between export default function () {} and export const x = () => {}, which is crucial for importing.
I was getting the same error. In my case, the problem wasn't in imports but in using styled-components' css attribute (using babel-plugin-styled-components).
<Button
variant={"color"}
// this caused it
css={`
margin-right: 24px;
`}
>
The workaround was to get rid of the css attribute by replacing the Button component with a new styled one. Like this:
const SubmitButton = styled(Button)`
margin-right: 24px;
`;
I got this error, I was working on an existing react library and making/editing things by hand. The solution was to find the index.ts in the src folder and add my new components to it manually.
Hope this helps someone
If you want to import your component using this syntax:
import { Button } from './button';
You can use named export from your button component
export { Button }
otherwise you have to import it without the curly braces like so:
import Button from './button';
you can read more about named vs default exports here:
https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
also this article has a nice explanation:
https://medium.com/#timoxley/named-exports-as-the-default-export-api-670b1b554f65

NextJS Back/Forward-button not working in Instagram Inline Safari

I have a website using NextJS, wherein all links are using next/link for routing. However, when I visit the website via Instagram inframe Safari, the back-/forward-buttons does not work for the routing. They are grey like I haven't clicked on a link.
I have tried using
<Link href={blablabla} passHref>
<a>
{content}
</a>
</Link>
But that does not solve the issue. Any thoughts on approach?
I also faced the problem.
I decided to not use next link on browser in Instagram app.
const link = (
<a href={href} target={target} rel={rel}>
{children}
</a>
);
// for browser in Instagram app (not working browser back on NextLink)
if (!navigator.userAgent.includes('Instagram')) return <NextLink href={href}>{link}</NextLink>;
return link;
Could you please provide either a link to the deployed site or some additional code for more context? Based on your problem, you could try a hybridized approach as follows:
import Link from 'next/link';
import { useRouter } from 'next/router';
//...
const slug: string = 'example';
const router = useRouter();
// use z-index to ensure clickable link is in very front of z-axis
// router.push('/instagram/[slug]', `instagram/${slug}`, {...}) === (href, as, { options })
const example = (
<div>
<Link
href='/instagram/[slug]'
as={`/instagram/${slug}`}
shallow={true}
prefetch={true}
passHref={true}
scroll={true}
>
<a
className='z-150 group-hover:bg-opacity-90'
id={'troublesome link'}
onClick={() =>
router.push('/instagram/[slug]', `/instagram/${slug}`, {
shallow: true,
locale: 'en-US',
scroll: true
})
}
>
<span className='sr-only'>
the larr and rarr notation below === left arrow and right
arrow, respectively. This text won't be visible on the DOM,
only for the screen reader
</span>
<p>← →</p>
</a>
</Link>
</div>
);
console.log(
`${
(example.props,
example.key,
example.type ?? 'example not yet rendered or error')
} `
);
//...
Note: Shallow routing only works if it is navigating between content hosted on the same page (even [...slug].tsx catch-all routes)
if this is the case, I definitely recommend utilizing shallow rendering
Another idea is to make all active-links a bright color to test whether they are indeed active (aria-active) or not in that particular DOM environment. Use an animation like ping to see if the aria-current clone is emitting light in the DOM.
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, {
FC,
JSXElementConstructor,
ReactElement
} from 'react';
import css from '../Button/button.module.css'
export const NavLink: FC = (children, href: string) => {
const child = React.Children.only(
children as ReactElement<
{ 'aria-current': string | null },
string | JSXElementConstructor<any>
>
);
const router = useRouter();
const AriaCurrentCloneElement = (
<Link href={href}>
{React.cloneElement(child, {
'aria-current': router.pathname === href ? 'page' : null
})}
</Link>
);
return <div className={css.clone}>{AriaCurrentCloneElement}</div>;
};
the corresponding css class, clone, is as follows:
.clone {
#apply flex justify-between;
& a {
#apply bg-current text-rojo-100 p-4 no-underline outline-none font-semibold;
&:hover {
#apply text-gray-900 duration-150 transition-colors ease-in-out transform-gpu;
}
&[aria-current] {
#apply animate-slowPing font-bold text-lg text-rojo-300 transition-transform duration-300 ease-in-out;
}
}
}

how change background color in different pages

i am two page in reactjs
pageOne.js:
import React from "react";
import { Link } from "react-router-dom";
import "./pageOne.css";
const PageOne = () => {
return (
<div>
one
<br />
<Link to="/pageTwo">Two Page</Link>
</div>
);
};
export default PageOne;
pageTwo.js:
import React from "react";
import { Link } from "react-router-dom";
import "./pageTwo.css";
const PageTwo = () => {
return (
<div>
two
<br />
<Link to="/">One Page</Link>
</div>
);
};
export default PageTwo;
i am define two css files for change background color when page loaded.
pageOne.css
body {
background-color: whitesmoke !important;
}
pageTwo.css
body {
background-color: crimson !important;
}
it's problem.in pageOne background color is crimson and in pageTwo background color is crimson.
sample
As I said earlier, there is only one body tag in the DOM tree by default. So when you try to style it whatever comes last will override the previous ones and in your case, the page two style will override the page one style.
To solve this, you got several options, but I will go with the easiest one. You can make a container for each of your pages and then assign a colour to that container to make the whole page background as you desired (You can simply make a layout component then wrap each of the components within it and with similar approach make it reusable). So, for example, you can create your first page like this:
<div className="crimson">
two
<br />
<Link to="/">one Page</Link>
</div>
and style it like this:
.crimson {
background-color: crimson;
min-height: 100vh; /* minimum height of page would be equal to available view-port height */
}
This goes the same for your other page. But you need to consider you have to remove the default margins from the body itself to prevent any disorder.
Working Demo:
I would solve this with Layout component:
const Layout = ({ backgroundColor = '#fff', children }) => (
<div style={{ backgroundColor }} className="layout">
{children}
</div>
)
then remove your css(and try not to use important in your css)
<Layout backgroundColor="#fff"><PageOne /></Layout>
and
<Layout backgroundColor="#f00"><PageTwo /></Layout>

Resources