"
49 | ],
50 | }
51 | }
--------------------------------------------------------------------------------
/LISCENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Aurora Scharff
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ßof the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js 14 Message Box
2 |
3 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
4 |
5 | It displays a messaging box using Next.js 14 with Server Actions, Tailwind CSS, and Prisma, and is progressively enhanced with React 19 features.
6 |
7 | A deployed, slightly altered version of the optimistic message box can be found [here](https://next15-remix-contacts-rebuild-v2.vercel.app/).
8 |
9 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
10 |
11 | ## Getting Started
12 |
13 | First, force install the dependencies to make the React 19 Beta work:
14 |
15 | ```bash
16 | npm install --force
17 | ```
18 |
19 | Then, run the development server:
20 |
21 | ```bash
22 | npm run dev
23 | # or
24 | yarn dev
25 | # or
26 | pnpm dev
27 | ```
28 |
29 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
30 |
31 | ## Prisma Setup
32 |
33 | You need decide between prisma local development with `sqlite` or a real database with for example `sqlserver`. Define it in the `schema.prisma` file.
34 |
35 | Consider adding a `.env` file to the root of the project and using these inside `schema.prisma` with `env("DATABASE_URL")`, refer to `.env.sample`.
36 |
37 | After switching, delete the `prisma/migrations` folder before running the migration command.
38 |
39 | When using sqlserver, you need to migrate the database schema with:
40 |
41 | ```bash
42 | npm run prisma.migrate
43 | ```
44 |
45 | When using sqllite, initialize with:
46 |
47 | ```bash
48 | npm run prisma.push
49 | ```
50 |
51 | Seed prisma/seed.ts for initial data:
52 |
53 | ```sh
54 | npm run prisma.seed
55 | ```
56 |
57 | ## Learn More
58 |
59 | To learn more about Next.js, take a look at the following resources:
60 |
61 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
62 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
63 |
64 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
65 |
66 | ## Deploy on Vercel
67 |
68 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
69 |
70 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
71 |
--------------------------------------------------------------------------------
/STEPS.md:
--------------------------------------------------------------------------------
1 | # DEMO STEPS
2 |
3 | ## Introduction
4 |
5 | - Thanks for the introduction
6 | - Aurora, web dev, norway, consultant at Inmeta in oslo, not meta, we had this name in 1996
7 | - I was able to step in for another speaker, and its been hectic getting here, but Im super excited to be here to be speaking here today. Will be demoing a practical example on working with forms and react server components.
8 | - Going to code a simplified version of something i’ve built for my customer project, where im actively using React Server Components, and it's working well for us.
9 |
10 | ## Setup and starting point
11 |
12 | - App router, prisma and local DB, tailwind CSS
13 | - This is now only server components. Show each component. Explain server components
14 | "- "but what I'm doing here can be done with client apps with a little more work to create endpoints"
15 | - Lets enhance this message box with rsc and react 19! Goal: make it interactive while minimizing js on the client and reducing forntend complexity.
16 |
17 | ## Basic form with server action
18 |
19 | (MessageInput + submitMessage)
20 |
21 | - Attach action prop using React's extension of the form element, auto transition
22 | - "this could be an action on the client, but since we're using server components, we can pass a server action instead"
23 | - Code submitMessage server action, data access layer
24 | - Submit to db, add hidden userId field. Mention .bind as an way to pass additional props. NB! Should be a part of the cookie or course and authentication but this is a demo, use auth or getcurrentuser.
25 | - RevalidatePath purge cache
26 |
27 | Notes: Lets start with the basic funcitonality. Make the form work and submit after reload with form action and hidden userId. When called with server action, behaves differently than native form, server action will have a post endpoint generated and be **exposed to the client** and can called without js. Then revalidatePath. “Just by doing that…”. Using native forms rather than buttons with onClicks, “had we used the onSubmit we would need React to have hydrated this page to be able to submit the form”.
28 |
29 | ## Add scroll handler
30 |
31 | (MessageBox)
32 |
33 | - Add donut pattern listener and explain, try it out
34 | - Show the devtools that the server components arent there but the scroller is
35 |
36 | Notes: Contains a children prop. Could be any prop. Can pass anything here, for example server components. Only the js for the scroll handler is loaded.
37 |
38 | ## Validate data
39 |
40 | (submitMessage)
41 |
42 | - Validate data with zod by moving object, throw error, use result in db insert, remove "as string"
43 | - Remove required on input
44 | - Add error boundary and show it triggering
45 | - Add back required on input
46 |
47 | Notes: Don't trust the input from the client. Handle errors however, for example error boundary. Show zod.
48 |
49 | ## Return validation
50 |
51 | (submitMessage)
52 |
53 | - Return instead of throw error and timestamp (create timestamp)
54 | - Add max limit messages sent 5.
55 | - Return success
56 | - Get this to the user: useActionState, add initial state and add span "errorMessage"
57 | - Show the error in the form.
58 | - Pass _prevState
59 |
60 | Notes: Could check for any requirements for your data. Create a component state when a form action is invoked. Can be called without js and return state without js. UseActionState returns a wrapped action, when called useActionState will return the last result of the action. Could use this to return the field errors.
61 |
62 | ## Toast message count
63 |
64 | (MessageInput)
65 |
66 | - useEffect to toast on error, depend on timestamp and error
67 | - Change span tag to noscript
68 |
69 | Notes: Noscript is a fallback.
70 |
71 | ## Return content for rollback on reset
72 |
73 | (MessageInput)
74 |
75 | - Explain form reset
76 | - Return result.data.content in the payload.
77 |
78 | Notes:
79 | React 19 the automatically resets uncontrolled inputs automatically after the action finishes. Follows the mpa form submission behavior. Probably used to using a library that would control forms, like react-hook-form. Not needed. Maintain the entered value when there is error. Maybe this could be changed to be valid. Let's return the content and set it as the defaultValue so it's not lost.
80 |
81 | ## Slow server action
82 |
83 | (submitMessage, MessageInput)
84 |
85 | - Add slow() to server action
86 | - Use third argument to show feedback
87 | - Increase max messages to 8 and demo again
88 |
89 | Notes: Realistic with a real db. Show feedback. We don't need to make an api endpoint and set error states etc like we used to in the next.js app router, which was a hassle.
90 |
91 | ## DEMO
92 |
93 | - By the way, this works without js!
94 | - Add some, we dont get automatic scrolling or button feedback or toasts, because all that requires js on the client.
95 | - Demo without js until it fails.
96 | - Turn js back on and show the feedback.
97 |
98 | ## Explanation
99 |
100 | - What we've been doing is progressively enhancing this, meaning ensuring the basic functionality works at the lowest level of resources, no javascript, then adding things on top to enhance the user experience for users with those resources available.
101 | - Lets say your user is on a slow device or slow connection and still waiting for js to finish downloading, parsing, or executing. This will work before its loaded, and will make the hydration for the JS that we do want load faster, because we reduced the amount of js on the client weaving the server component and the client together. Now depending on the user’s situation, they will get the better experience, and always have a form that works.
102 |
103 | ## Replace with submitButton
104 |
105 | (SubmitButton + MessageBox)
106 |
107 | - Reuse this logic
108 | - Extract button to submitbutton with useformstatus and spinner
109 | - Say you can generalize this better, extend button element
110 | - Add new button to the rsc-header and code the server action, inline server action: "use server", slow, delete, revalidate
111 |
112 | Completely composable and standardized SubmitButton for our application. Pretty amazing. Power of RSC.
113 |
114 | ## Optimistic update
115 |
116 | - Stash current code
117 | - Switch branch
118 | - Show code for messagebox and messages
119 | - Show messageInput and explain how it works, action fallback
120 | - Send multiple messages slowly, then many until it fails
121 |
122 | Notes: Can even enhance this further with optimistic updates. This still works without js. Adding an onSubmit for client-side js only functionality, use a state with defaultvalue maintain the progressive enhancement.
123 |
124 | Of course, depending on your app you can decide how to implement forms and whether you still want your react-hook form and whatnot, but by using the the more primitive features of the web together with React 19 and React Server Components, we can make our forms very robust and while maintaining a great user experience. And there is alot more to come from these. They will be primitives for libraries simpliying things for developers, focus on building apps.
125 |
126 | That's it for this demo, the code is pinned on my GitHub and the optimistic update is on a branch, and follow me on Twitter if you are interested in more rsc content. Thanks for listening and thanks React Universe Conf!
127 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/super-dev03/next-message-box/d8c4e699ce8e556e28f87b52cfeccee6f901188c/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import './globals.css';
2 | import { Inter } from 'next/font/google';
3 | import type { Metadata } from 'next';
4 |
5 | const inter = Inter({ subsets: ['latin'] });
6 |
7 | export const metadata: Metadata = {
8 | description: 'Next.js 14 message box example using server actions and useActionState',
9 | title: 'Next.js 14 Message Box',
10 | };
11 |
12 | export default function RootLayout({ children }: { children: React.ReactNode }) {
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Toaster } from 'react-hot-toast';
3 | import MessageBox from '@/components/message-box/MessageBox';
4 |
5 | export default function Home() {
6 | return (
7 | <>
8 |
9 |