feat(templates): website optimisations for image sizes and loading (#9447)
Adds defaultPopulate on pages and posts Further optimises images and fetching staticParams
This commit is contained in:
@@ -6,6 +6,7 @@ import type { Footer } from '@/payload-types'
|
||||
|
||||
import { ThemeSelector } from '@/providers/Theme/ThemeSelector'
|
||||
import { CMSLink } from '@/components/Link'
|
||||
import { Logo } from '@/components/Logo/Logo'
|
||||
|
||||
export async function Footer() {
|
||||
const footer: Footer = await getCachedGlobal('footer', 1)()
|
||||
@@ -16,15 +17,7 @@ export async function Footer() {
|
||||
<footer className="border-t border-border bg-black dark:bg-card text-white">
|
||||
<div className="container py-8 gap-8 flex flex-col md:flex-row md:justify-between">
|
||||
<Link className="flex items-center" href="/">
|
||||
<picture>
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
width={193}
|
||||
height={43}
|
||||
className="max-w-[6rem] invert-0 w-full"
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-logo-light.svg"
|
||||
/>
|
||||
</picture>
|
||||
<Logo />
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col-reverse items-start md:flex-row gap-4 md:items-center">
|
||||
|
||||
@@ -33,7 +33,7 @@ export const HeaderClient: React.FC<HeaderClientProps> = ({ header }) => {
|
||||
<header className="container relative z-20 " {...(theme ? { 'data-theme': theme } : {})}>
|
||||
<div className="py-8 border-b border-border flex justify-between">
|
||||
<Link href="/">
|
||||
<Logo />
|
||||
<Logo loading="eager" priority="high" />
|
||||
</Link>
|
||||
<HeaderNav header={header} />
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,9 @@ export async function generateStaticParams() {
|
||||
draft: false,
|
||||
limit: 1000,
|
||||
overrideAccess: false,
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
})
|
||||
|
||||
const params = pages.docs
|
||||
|
||||
@@ -21,6 +21,9 @@ export async function generateStaticParams() {
|
||||
draft: false,
|
||||
limit: 1000,
|
||||
overrideAccess: false,
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
})
|
||||
|
||||
const params = posts.docs.map(({ slug }) => {
|
||||
|
||||
@@ -30,6 +30,12 @@ export const Pages: CollectionConfig = {
|
||||
read: authenticatedOrPublished,
|
||||
update: authenticated,
|
||||
},
|
||||
// This config controls what's populated by default when a page is referenced
|
||||
// https://payloadcms.com/docs/queries/select#defaultpopulate-collection-config-property
|
||||
defaultPopulate: {
|
||||
title: true,
|
||||
slug: true,
|
||||
},
|
||||
admin: {
|
||||
defaultColumns: ['title', 'slug', 'updatedAt'],
|
||||
livePreview: {
|
||||
|
||||
@@ -36,6 +36,17 @@ export const Posts: CollectionConfig = {
|
||||
read: authenticatedOrPublished,
|
||||
update: authenticated,
|
||||
},
|
||||
// This config controls what's populated by default when a post is referenced
|
||||
// https://payloadcms.com/docs/queries/select#defaultpopulate-collection-config-property
|
||||
defaultPopulate: {
|
||||
title: true,
|
||||
slug: true,
|
||||
categories: true,
|
||||
meta: {
|
||||
image: true,
|
||||
description: true,
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
defaultColumns: ['title', 'slug', 'updatedAt'],
|
||||
livePreview: {
|
||||
|
||||
@@ -37,7 +37,7 @@ export const Card: React.FC<{
|
||||
>
|
||||
<div className="relative w-full ">
|
||||
{!metaImage && <div className="">No image</div>}
|
||||
{metaImage && typeof metaImage !== 'string' && <Media resource={metaImage} size="360px" />}
|
||||
{metaImage && typeof metaImage !== 'string' && <Media resource={metaImage} size="33vw" />}
|
||||
</div>
|
||||
<div className="p-4">
|
||||
{showCategories && hasCategories && (
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
import React from 'react'
|
||||
|
||||
export const Logo = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
loading?: 'lazy' | 'eager'
|
||||
priority?: 'auto' | 'high' | 'low'
|
||||
}
|
||||
|
||||
export const Logo = (props: Props) => {
|
||||
const { loading: loadingFromProps, priority: priorityFromProps } = props
|
||||
|
||||
const loading = loadingFromProps || 'lazy'
|
||||
const priority = priorityFromProps || 'low'
|
||||
|
||||
return (
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
width={193}
|
||||
height={43}
|
||||
loading={loading}
|
||||
fetchPriority={priority}
|
||||
decoding="async"
|
||||
className="max-w-[9.375rem] invert dark:invert-0 w-full"
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-logo-light.svg"
|
||||
/>
|
||||
|
||||
@@ -8,30 +8,27 @@ import React from 'react'
|
||||
|
||||
import type { Props as MediaProps } from '../types'
|
||||
|
||||
import cssVariables from '@/cssVariables'
|
||||
import { cssVariables } from '@/cssVariables'
|
||||
import { getClientSideURL } from '@/utilities/getURL'
|
||||
|
||||
const { breakpoints } = cssVariables
|
||||
|
||||
// A base64 encoded image to use as a placeholder while the image is loading
|
||||
const placeholderBlur =
|
||||
'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'
|
||||
'data:image/png;base64,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'
|
||||
|
||||
export const ImageMedia: React.FC<MediaProps> = (props) => {
|
||||
const {
|
||||
alt: altFromProps,
|
||||
fill,
|
||||
imgClassName,
|
||||
onClick,
|
||||
onLoad: onLoadFromProps,
|
||||
priority,
|
||||
resource,
|
||||
size: sizeFromProps,
|
||||
src: srcFromProps,
|
||||
loading: loadingFromProps,
|
||||
} = props
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState(true)
|
||||
|
||||
let width: number | undefined
|
||||
let height: number | undefined
|
||||
let alt = altFromProps
|
||||
@@ -53,33 +50,31 @@ export const ImageMedia: React.FC<MediaProps> = (props) => {
|
||||
src = `${getClientSideURL()}${url}`
|
||||
}
|
||||
|
||||
const loading = loadingFromProps || 'lazy'
|
||||
|
||||
// NOTE: this is used by the browser to determine which image to download at different screen sizes
|
||||
const sizes = sizeFromProps
|
||||
? sizeFromProps
|
||||
: Object.entries(breakpoints)
|
||||
.map(([, value]) => `(max-width: ${value}px) ${value}px`)
|
||||
.map(([, value]) => `(max-width: ${value}px) ${value * 2}w`)
|
||||
.join(', ')
|
||||
|
||||
return (
|
||||
<NextImage
|
||||
alt={alt || ''}
|
||||
className={cn(imgClassName)}
|
||||
fill={fill}
|
||||
height={!fill ? height : undefined}
|
||||
onClick={onClick}
|
||||
onLoad={() => {
|
||||
setIsLoading(false)
|
||||
if (typeof onLoadFromProps === 'function') {
|
||||
onLoadFromProps()
|
||||
}
|
||||
}}
|
||||
placeholder="blur"
|
||||
blurDataURL={placeholderBlur}
|
||||
priority={priority}
|
||||
quality={90}
|
||||
sizes={sizes}
|
||||
src={src}
|
||||
width={!fill ? width : undefined}
|
||||
/>
|
||||
<picture>
|
||||
<NextImage
|
||||
alt={alt || ''}
|
||||
className={cn(imgClassName)}
|
||||
fill={fill}
|
||||
height={!fill ? height : undefined}
|
||||
placeholder="blur"
|
||||
blurDataURL={placeholderBlur}
|
||||
priority={priority}
|
||||
quality={100}
|
||||
loading={loading}
|
||||
sizes={sizes}
|
||||
src={src}
|
||||
width={!fill ? width : undefined}
|
||||
/>
|
||||
</picture>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface Props {
|
||||
imgClassName?: string
|
||||
onClick?: () => void
|
||||
onLoad?: () => void
|
||||
loading?: 'lazy' | 'eager' // for NextImage only
|
||||
priority?: boolean // for NextImage only
|
||||
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
|
||||
resource?: MediaType | string | number // for Payload media
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
// Keep these in sync with the CSS variables in the `_css` directory
|
||||
// Keep these in sync with the CSS variables in your tailwind configuration
|
||||
|
||||
const cssVariables = {
|
||||
export const cssVariables = {
|
||||
breakpoints: {
|
||||
l: 1440,
|
||||
m: 1024,
|
||||
s: 768,
|
||||
},
|
||||
colors: {
|
||||
base0: 'rgb(255, 255, 255)',
|
||||
base100: 'rgb(235, 235, 235)',
|
||||
base500: 'rgb(128, 128, 128)',
|
||||
base850: 'rgb(34, 34, 34)',
|
||||
base1000: 'rgb(0, 0, 0)',
|
||||
error500: 'rgb(255, 111, 118)',
|
||||
'3xl': 1920,
|
||||
'2xl': 1536,
|
||||
xl: 1280,
|
||||
lg: 1024,
|
||||
md: 768,
|
||||
sm: 640,
|
||||
},
|
||||
}
|
||||
|
||||
export default cssVariables
|
||||
|
||||
@@ -92,7 +92,7 @@ export const home: RequiredDataFromCollectionSlug<'pages'> = {
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'here',
|
||||
text: 'on our Github',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 753 KiB |
@@ -38,10 +38,13 @@ export const HighImpactHero: React.FC<Page['hero']> = ({ links, media, richText
|
||||
</div>
|
||||
<div className="min-h-[80vh] select-none">
|
||||
{media && typeof media === 'object' && (
|
||||
<React.Fragment>
|
||||
<Media fill imgClassName="-z-10 object-cover" resource={media} />
|
||||
{/* <div className="absolute pointer-events-none left-0 bottom-0 w-full h-1/2 bg-gradient-to-t from-black to-transparent" /> */}
|
||||
</React.Fragment>
|
||||
<Media
|
||||
fill
|
||||
imgClassName="-z-10 object-cover"
|
||||
priority={false}
|
||||
loading="lazy"
|
||||
resource={media}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@ import type { Page, Post } from '../payload-types'
|
||||
import { mergeOpenGraph } from './mergeOpenGraph'
|
||||
import { getServerSideURL } from './getURL'
|
||||
|
||||
export const generateMeta = async (args: { doc: Page | Post }): Promise<Metadata> => {
|
||||
export const generateMeta = async (args: {
|
||||
doc: Partial<Page> | Partial<Post>
|
||||
}): Promise<Metadata> => {
|
||||
const { doc } = args || {}
|
||||
|
||||
const ogImage =
|
||||
|
||||
Reference in New Issue
Block a user