Skip to content

Commit e65a04a

Browse files
authored
templates: adds landing page to blank template (#10769)
This addition enhances the `Blank` template by adding a simple front-end to ensure a better out-of-the-box experience. When deploying the template to platforms like `Payload Cloud`, `Vercel`, or similar services, users would previously encounter a `404` or `not-found` page on the front-end `/` route unless explicitly handled. With this update, the template now includes a minimal front-end that renders a basic page at route `/`. ### Notes - The added front-end is entirely optional. - If users prefer to use the `Blank` template as a starting point for a back-end-only solution or plan to integrate with a different front-end framework, they can simply delete the `(frontend)` folder and proceed as before. `Logged out`: ![Screenshot 2025-01-28 at 10 26 01 AM](https://github.com/user-attachments/assets/f6cd99bd-9746-4d0e-910f-2322a671c6b3) `Logged in`: ![Screenshot 2025-01-28 at 10 25 42 AM](https://github.com/user-attachments/assets/27c0bbfb-bd94-4e3c-9bb9-332aa3ccc8cc) `Mobile`: ![Screenshot 2025-01-28 at 10 25 14 AM](https://github.com/user-attachments/assets/370869b4-c5e5-4b17-bff6-3514e7baffc7)
1 parent 57f7218 commit e65a04a

File tree

18 files changed

+1516
-0
lines changed

18 files changed

+1516
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react'
2+
import './styles.css'
3+
4+
export const metadata = {
5+
description: 'A blank template using Payload in a Next.js app.',
6+
title: 'Payload Blank Template',
7+
}
8+
9+
export default async function RootLayout(props: { children: React.ReactNode }) {
10+
const { children } = props
11+
12+
return (
13+
<html lang="en">
14+
<body>
15+
<main>{children}</main>
16+
</body>
17+
</html>
18+
)
19+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { headers as getHeaders } from 'next/headers.js'
2+
import Image from 'next/image'
3+
import { getPayload } from 'payload'
4+
import React from 'react'
5+
import { fileURLToPath } from 'url'
6+
7+
import config from '@/payload.config'
8+
import './styles.css'
9+
10+
export default async function HomePage() {
11+
const headers = await getHeaders()
12+
const payloadConfig = await config
13+
const payload = await getPayload({ config: payloadConfig })
14+
const { user } = await payload.auth({ headers })
15+
16+
const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
17+
18+
return (
19+
<div className="home">
20+
<div className="content">
21+
<picture>
22+
<source srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg" />
23+
<Image
24+
alt="Payload Logo"
25+
height={65}
26+
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg"
27+
width={65}
28+
/>
29+
</picture>
30+
{!user && <h1>Welcome to your new project.</h1>}
31+
{user && <h1>Welcome back, {user.email}</h1>}
32+
<div className="links">
33+
<a
34+
className="admin"
35+
href={payloadConfig.routes.admin}
36+
rel="noopener noreferrer"
37+
target="_blank"
38+
>
39+
Go to admin panel
40+
</a>
41+
<a
42+
className="docs"
43+
href="https://payloadcms.com/docs"
44+
rel="noopener noreferrer"
45+
target="_blank"
46+
>
47+
Documentation
48+
</a>
49+
</div>
50+
</div>
51+
<div className="footer">
52+
<p>Update this page by editing</p>
53+
<a className="codeLink" href={fileURL}>
54+
<code>app/(frontend)/page.tsx</code>
55+
</a>
56+
</div>
57+
</div>
58+
)
59+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
:root {
2+
--font-mono: 'Roboto Mono', monospace;
3+
}
4+
5+
* {
6+
box-sizing: border-box;
7+
}
8+
9+
html {
10+
font-size: 18px;
11+
line-height: 32px;
12+
13+
background: rgb(0, 0, 0);
14+
-webkit-font-smoothing: antialiased;
15+
}
16+
17+
html,
18+
body,
19+
#app {
20+
height: 100%;
21+
}
22+
23+
body {
24+
font-family: system-ui;
25+
font-size: 18px;
26+
line-height: 32px;
27+
28+
margin: 0;
29+
color: rgb(1000, 1000, 1000);
30+
31+
@media (max-width: 1024px) {
32+
font-size: 15px;
33+
line-height: 24px;
34+
}
35+
}
36+
37+
img {
38+
max-width: 100%;
39+
height: auto;
40+
display: block;
41+
}
42+
43+
h1 {
44+
margin: 40px 0;
45+
font-size: 64px;
46+
line-height: 70px;
47+
font-weight: bold;
48+
49+
@media (max-width: 1024px) {
50+
margin: 24px 0;
51+
font-size: 42px;
52+
line-height: 42px;
53+
}
54+
55+
@media (max-width: 768px) {
56+
font-size: 38px;
57+
line-height: 38px;
58+
}
59+
60+
@media (max-width: 400px) {
61+
font-size: 32px;
62+
line-height: 32px;
63+
}
64+
}
65+
66+
p {
67+
margin: 24px 0;
68+
69+
@media (max-width: 1024px) {
70+
margin: calc(var(--base) * 0.75) 0;
71+
}
72+
}
73+
74+
a {
75+
color: currentColor;
76+
77+
&:focus {
78+
opacity: 0.8;
79+
outline: none;
80+
}
81+
82+
&:active {
83+
opacity: 0.7;
84+
outline: none;
85+
}
86+
}
87+
88+
svg {
89+
vertical-align: middle;
90+
}
91+
92+
.home {
93+
display: flex;
94+
flex-direction: column;
95+
justify-content: space-between;
96+
align-items: center;
97+
height: 100vh;
98+
padding: 45px;
99+
max-width: 1024px;
100+
margin: 0 auto;
101+
overflow: hidden;
102+
103+
@media (max-width: 400px) {
104+
padding: 24px;
105+
}
106+
107+
.content {
108+
display: flex;
109+
flex-direction: column;
110+
align-items: center;
111+
justify-content: center;
112+
flex-grow: 1;
113+
114+
h1 {
115+
text-align: center;
116+
}
117+
}
118+
119+
.links {
120+
display: flex;
121+
align-items: center;
122+
gap: 12px;
123+
124+
a {
125+
text-decoration: none;
126+
padding: 0.25rem 0.5rem;
127+
border-radius: 4px;
128+
}
129+
130+
.admin {
131+
color: rgb(0, 0, 0);
132+
background: rgb(1000, 1000, 1000);
133+
border: 1px solid rgb(0, 0, 0);
134+
}
135+
136+
.docs {
137+
color: rgb(1000, 1000, 1000);
138+
background: rgb(0, 0, 0);
139+
border: 1px solid rgb(1000, 1000, 1000);
140+
}
141+
}
142+
143+
.footer {
144+
display: flex;
145+
align-items: center;
146+
gap: 8px;
147+
148+
@media (max-width: 1024px) {
149+
flex-direction: column;
150+
gap: 6px;
151+
}
152+
153+
p {
154+
margin: 0;
155+
}
156+
157+
.codeLink {
158+
text-decoration: none;
159+
padding: 0 0.5rem;
160+
background: rgb(60, 60, 60);
161+
border-radius: 4px;
162+
}
163+
}
164+
}

templates/blank/src/payload-types.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,31 @@ export interface Config {
1313
collections: {
1414
users: User;
1515
media: Media;
16+
'payload-locked-documents': PayloadLockedDocument;
1617
'payload-preferences': PayloadPreference;
1718
'payload-migrations': PayloadMigration;
1819
};
20+
collectionsJoins: {};
21+
collectionsSelect: {
22+
users: UsersSelect<false> | UsersSelect<true>;
23+
media: MediaSelect<false> | MediaSelect<true>;
24+
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
25+
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
26+
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
27+
};
1928
db: {
2029
defaultIDType: string;
2130
};
2231
globals: {};
32+
globalsSelect: {};
2333
locale: null;
2434
user: User & {
2535
collection: 'users';
2636
};
37+
jobs: {
38+
tasks: unknown;
39+
workflows: unknown;
40+
};
2741
}
2842
export interface UserAuthOperations {
2943
forgotPassword: {
@@ -79,6 +93,29 @@ export interface Media {
7993
focalX?: number | null;
8094
focalY?: number | null;
8195
}
96+
/**
97+
* This interface was referenced by `Config`'s JSON-Schema
98+
* via the `definition` "payload-locked-documents".
99+
*/
100+
export interface PayloadLockedDocument {
101+
id: string;
102+
document?:
103+
| ({
104+
relationTo: 'users';
105+
value: string | User;
106+
} | null)
107+
| ({
108+
relationTo: 'media';
109+
value: string | Media;
110+
} | null);
111+
globalSlug?: string | null;
112+
user: {
113+
relationTo: 'users';
114+
value: string | User;
115+
};
116+
updatedAt: string;
117+
createdAt: string;
118+
}
82119
/**
83120
* This interface was referenced by `Config`'s JSON-Schema
84121
* via the `definition` "payload-preferences".
@@ -113,6 +150,71 @@ export interface PayloadMigration {
113150
updatedAt: string;
114151
createdAt: string;
115152
}
153+
/**
154+
* This interface was referenced by `Config`'s JSON-Schema
155+
* via the `definition` "users_select".
156+
*/
157+
export interface UsersSelect<T extends boolean = true> {
158+
updatedAt?: T;
159+
createdAt?: T;
160+
email?: T;
161+
resetPasswordToken?: T;
162+
resetPasswordExpiration?: T;
163+
salt?: T;
164+
hash?: T;
165+
loginAttempts?: T;
166+
lockUntil?: T;
167+
}
168+
/**
169+
* This interface was referenced by `Config`'s JSON-Schema
170+
* via the `definition` "media_select".
171+
*/
172+
export interface MediaSelect<T extends boolean = true> {
173+
alt?: T;
174+
updatedAt?: T;
175+
createdAt?: T;
176+
url?: T;
177+
thumbnailURL?: T;
178+
filename?: T;
179+
mimeType?: T;
180+
filesize?: T;
181+
width?: T;
182+
height?: T;
183+
focalX?: T;
184+
focalY?: T;
185+
}
186+
/**
187+
* This interface was referenced by `Config`'s JSON-Schema
188+
* via the `definition` "payload-locked-documents_select".
189+
*/
190+
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
191+
document?: T;
192+
globalSlug?: T;
193+
user?: T;
194+
updatedAt?: T;
195+
createdAt?: T;
196+
}
197+
/**
198+
* This interface was referenced by `Config`'s JSON-Schema
199+
* via the `definition` "payload-preferences_select".
200+
*/
201+
export interface PayloadPreferencesSelect<T extends boolean = true> {
202+
user?: T;
203+
key?: T;
204+
value?: T;
205+
updatedAt?: T;
206+
createdAt?: T;
207+
}
208+
/**
209+
* This interface was referenced by `Config`'s JSON-Schema
210+
* via the `definition` "payload-migrations_select".
211+
*/
212+
export interface PayloadMigrationsSelect<T extends boolean = true> {
213+
name?: T;
214+
batch?: T;
215+
updatedAt?: T;
216+
createdAt?: T;
217+
}
116218
/**
117219
* This interface was referenced by `Config`'s JSON-Schema
118220
* via the `definition` "auth".
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react'
2+
import './styles.css'
3+
4+
export const metadata = {
5+
description: 'A blank template using Payload in a Next.js app.',
6+
title: 'Payload Blank Template',
7+
}
8+
9+
export default async function RootLayout(props: { children: React.ReactNode }) {
10+
const { children } = props
11+
12+
return (
13+
<html lang="en">
14+
<body>
15+
<main>{children}</main>
16+
</body>
17+
</html>
18+
)
19+
}

0 commit comments

Comments
 (0)