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 { ThemeSelector } from '@/providers/Theme/ThemeSelector'
|
||||||
import { CMSLink } from '@/components/Link'
|
import { CMSLink } from '@/components/Link'
|
||||||
|
import { Logo } from '@/components/Logo/Logo'
|
||||||
|
|
||||||
export async function Footer() {
|
export async function Footer() {
|
||||||
const footer: Footer = await getCachedGlobal('footer', 1)()
|
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">
|
<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">
|
<div className="container py-8 gap-8 flex flex-col md:flex-row md:justify-between">
|
||||||
<Link className="flex items-center" href="/">
|
<Link className="flex items-center" href="/">
|
||||||
<picture>
|
<Logo />
|
||||||
<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>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex flex-col-reverse items-start md:flex-row gap-4 md:items-center">
|
<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 } : {})}>
|
<header className="container relative z-20 " {...(theme ? { 'data-theme': theme } : {})}>
|
||||||
<div className="py-8 border-b border-border flex justify-between">
|
<div className="py-8 border-b border-border flex justify-between">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<Logo />
|
<Logo loading="eager" priority="high" />
|
||||||
</Link>
|
</Link>
|
||||||
<HeaderNav header={header} />
|
<HeaderNav header={header} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export async function generateStaticParams() {
|
|||||||
draft: false,
|
draft: false,
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
overrideAccess: false,
|
overrideAccess: false,
|
||||||
|
select: {
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = pages.docs
|
const params = pages.docs
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export async function generateStaticParams() {
|
|||||||
draft: false,
|
draft: false,
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
overrideAccess: false,
|
overrideAccess: false,
|
||||||
|
select: {
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = posts.docs.map(({ slug }) => {
|
const params = posts.docs.map(({ slug }) => {
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ export const Pages: CollectionConfig = {
|
|||||||
read: authenticatedOrPublished,
|
read: authenticatedOrPublished,
|
||||||
update: authenticated,
|
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: {
|
admin: {
|
||||||
defaultColumns: ['title', 'slug', 'updatedAt'],
|
defaultColumns: ['title', 'slug', 'updatedAt'],
|
||||||
livePreview: {
|
livePreview: {
|
||||||
|
|||||||
@@ -36,6 +36,17 @@ export const Posts: CollectionConfig = {
|
|||||||
read: authenticatedOrPublished,
|
read: authenticatedOrPublished,
|
||||||
update: authenticated,
|
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: {
|
admin: {
|
||||||
defaultColumns: ['title', 'slug', 'updatedAt'],
|
defaultColumns: ['title', 'slug', 'updatedAt'],
|
||||||
livePreview: {
|
livePreview: {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const Card: React.FC<{
|
|||||||
>
|
>
|
||||||
<div className="relative w-full ">
|
<div className="relative w-full ">
|
||||||
{!metaImage && <div className="">No image</div>}
|
{!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>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
{showCategories && hasCategories && (
|
{showCategories && hasCategories && (
|
||||||
|
|||||||
@@ -1,12 +1,26 @@
|
|||||||
import React from 'react'
|
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 (
|
return (
|
||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
<img
|
<img
|
||||||
alt="Payload Logo"
|
alt="Payload Logo"
|
||||||
width={193}
|
width={193}
|
||||||
height={43}
|
height={43}
|
||||||
|
loading={loading}
|
||||||
|
fetchPriority={priority}
|
||||||
|
decoding="async"
|
||||||
className="max-w-[9.375rem] invert dark:invert-0 w-full"
|
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"
|
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 type { Props as MediaProps } from '../types'
|
||||||
|
|
||||||
import cssVariables from '@/cssVariables'
|
import { cssVariables } from '@/cssVariables'
|
||||||
import { getClientSideURL } from '@/utilities/getURL'
|
import { getClientSideURL } from '@/utilities/getURL'
|
||||||
|
|
||||||
const { breakpoints } = cssVariables
|
const { breakpoints } = cssVariables
|
||||||
|
|
||||||
// A base64 encoded image to use as a placeholder while the image is loading
|
// A base64 encoded image to use as a placeholder while the image is loading
|
||||||
const placeholderBlur =
|
const placeholderBlur =
|
||||||
'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'
|
'data:image/png;base64,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'
|
||||||
|
|
||||||
export const ImageMedia: React.FC<MediaProps> = (props) => {
|
export const ImageMedia: React.FC<MediaProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
alt: altFromProps,
|
alt: altFromProps,
|
||||||
fill,
|
fill,
|
||||||
imgClassName,
|
imgClassName,
|
||||||
onClick,
|
|
||||||
onLoad: onLoadFromProps,
|
|
||||||
priority,
|
priority,
|
||||||
resource,
|
resource,
|
||||||
size: sizeFromProps,
|
size: sizeFromProps,
|
||||||
src: srcFromProps,
|
src: srcFromProps,
|
||||||
|
loading: loadingFromProps,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = React.useState(true)
|
|
||||||
|
|
||||||
let width: number | undefined
|
let width: number | undefined
|
||||||
let height: number | undefined
|
let height: number | undefined
|
||||||
let alt = altFromProps
|
let alt = altFromProps
|
||||||
@@ -53,33 +50,31 @@ export const ImageMedia: React.FC<MediaProps> = (props) => {
|
|||||||
src = `${getClientSideURL()}${url}`
|
src = `${getClientSideURL()}${url}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loading = loadingFromProps || 'lazy'
|
||||||
|
|
||||||
// NOTE: this is used by the browser to determine which image to download at different screen sizes
|
// NOTE: this is used by the browser to determine which image to download at different screen sizes
|
||||||
const sizes = sizeFromProps
|
const sizes = sizeFromProps
|
||||||
? sizeFromProps
|
? sizeFromProps
|
||||||
: Object.entries(breakpoints)
|
: Object.entries(breakpoints)
|
||||||
.map(([, value]) => `(max-width: ${value}px) ${value}px`)
|
.map(([, value]) => `(max-width: ${value}px) ${value * 2}w`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextImage
|
<picture>
|
||||||
alt={alt || ''}
|
<NextImage
|
||||||
className={cn(imgClassName)}
|
alt={alt || ''}
|
||||||
fill={fill}
|
className={cn(imgClassName)}
|
||||||
height={!fill ? height : undefined}
|
fill={fill}
|
||||||
onClick={onClick}
|
height={!fill ? height : undefined}
|
||||||
onLoad={() => {
|
placeholder="blur"
|
||||||
setIsLoading(false)
|
blurDataURL={placeholderBlur}
|
||||||
if (typeof onLoadFromProps === 'function') {
|
priority={priority}
|
||||||
onLoadFromProps()
|
quality={100}
|
||||||
}
|
loading={loading}
|
||||||
}}
|
sizes={sizes}
|
||||||
placeholder="blur"
|
src={src}
|
||||||
blurDataURL={placeholderBlur}
|
width={!fill ? width : undefined}
|
||||||
priority={priority}
|
/>
|
||||||
quality={90}
|
</picture>
|
||||||
sizes={sizes}
|
|
||||||
src={src}
|
|
||||||
width={!fill ? width : undefined}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface Props {
|
|||||||
imgClassName?: string
|
imgClassName?: string
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
onLoad?: () => void
|
onLoad?: () => void
|
||||||
|
loading?: 'lazy' | 'eager' // for NextImage only
|
||||||
priority?: boolean // for NextImage only
|
priority?: boolean // for NextImage only
|
||||||
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
|
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
|
||||||
resource?: MediaType | string | number // for Payload media
|
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: {
|
breakpoints: {
|
||||||
l: 1440,
|
'3xl': 1920,
|
||||||
m: 1024,
|
'2xl': 1536,
|
||||||
s: 768,
|
xl: 1280,
|
||||||
},
|
lg: 1024,
|
||||||
colors: {
|
md: 768,
|
||||||
base0: 'rgb(255, 255, 255)',
|
sm: 640,
|
||||||
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)',
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default cssVariables
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export const home: RequiredDataFromCollectionSlug<'pages'> = {
|
|||||||
format: 0,
|
format: 0,
|
||||||
mode: 'normal',
|
mode: 'normal',
|
||||||
style: '',
|
style: '',
|
||||||
text: 'here',
|
text: 'on our Github',
|
||||||
version: 1,
|
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>
|
||||||
<div className="min-h-[80vh] select-none">
|
<div className="min-h-[80vh] select-none">
|
||||||
{media && typeof media === 'object' && (
|
{media && typeof media === 'object' && (
|
||||||
<React.Fragment>
|
<Media
|
||||||
<Media fill imgClassName="-z-10 object-cover" resource={media} />
|
fill
|
||||||
{/* <div className="absolute pointer-events-none left-0 bottom-0 w-full h-1/2 bg-gradient-to-t from-black to-transparent" /> */}
|
imgClassName="-z-10 object-cover"
|
||||||
</React.Fragment>
|
priority={false}
|
||||||
|
loading="lazy"
|
||||||
|
resource={media}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import type { Page, Post } from '../payload-types'
|
|||||||
import { mergeOpenGraph } from './mergeOpenGraph'
|
import { mergeOpenGraph } from './mergeOpenGraph'
|
||||||
import { getServerSideURL } from './getURL'
|
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 { doc } = args || {}
|
||||||
|
|
||||||
const ogImage =
|
const ogImage =
|
||||||
|
|||||||
Reference in New Issue
Block a user