The modern web would be different without rich client-side applications supported by powerful frameworks: React, Angular, Vue, Lit, and many others. These frameworks rely on client-side JavaScript, which is their core. However, there are other approaches to rendering. One of them (quite old, by the way) is server-side rendering entirely without JavaScript. Letβs find out if this is a good idea and how Remix can help us with it?
VSCode, but also cloud IDEs such as codesandbox (other IDEs are also ok)
Software Engineer, Netherlands
My primary interest is self development and craftsmanship. I enjoy exploring technologies, coding open source and enterprise projects, teaching, speaking and writing about programming - JavaScript, Node.js, TypeScript, Go, Java, Docker, Kubernetes, JSON Schema, DevOps, Web Components, Algorithms π β½οΈ π§βπ» π§

Software Engineer, Netherlands
JavaScript developer with full-stack experience and frontend passion. He runs a small development agency codeville.agency and likes to talk about technologies they use: React, Remix and Serverless.
a modern full stack web framework
import { json } from "@remix-run/node"; // or cloudflare/deno
export async function loader() {
  const res = await fetch("https://api.github.com/gists");
  const gists = await res.json();
  return json(
    gists.map((gist) => ({
      description: gist.description,
      url: gist.html_url,
      files: Object.keys(gist.files),
      owner: gist.owner.login,
    }))
  );
}
export default function Gists() {
  const gists = useLoaderData<typeof loader>();
  return (
    <ul>
      {gists.map((gist) => (
        <li key={gist.id}>
          <a href={gist.url}>
            {gist.description}, {gist.owner}
          </a>
          <ul>
            {gist.files.map((key) => (
              <li key={key}>{key}</li>
            ))}
          </ul>
        </li>
      ))}
    </ul>
  );
}

a compiler for React Router

compiler - server side and client side app, alongside with manifest meta information
server
loader, action & default component// https://github.com/remix-run/examples/blob/main/_official-blog-tutorial/app/routes/posts/index.tsx
import { json } from "@remix-run/node"
import { Link, useLoaderData } from "@remix-run/react"
import { getPosts } from "~/models/post.server"
export const loader = async () => json({ posts: await getPosts() })
export default function Posts() {
  const { posts } = useLoaderData<typeof loader>()
  return (
    <main>
      <h1>Posts</h1>
      <Link to="admin" className="text-red-600 underline">Admin</Link>
      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <Link to={post.slug} className="text-blue-600 underline">{post.title}</Link>
          </li>
        ))}
      </ul>
    </main>
  )
}
import { createPost } from "~/models/post.server"
export const action = async ({ request }: ActionArgs) => {
  const formData = await request.formData()
  const title = formData.get("title")
  if (!title) return json({ error: "Title is required" })
  await createPost({ title })
  return redirect("/posts/admin")
};
export default function NewPost() {
  const transition = useTransition()
  const isCreating = Boolean(transition.submission)
  return (
    <Form method="post">
      <label>
        Post Title:{" "}<input type="text" name="title" />
      </label>
      <button type="submit" disabled={isCreating}>
        {isCreating ? "Creating..." : "Create Post"}
      </button>
    </Form>
  );
}

app/
βββ routes/
β   βββ about.tsx           // route is based on the static path
β   βββ blog/               // route has an additional "/blog" segment in the URL
β   β   βββ $postId.tsx     // dynamic params
β   β   βββ categories.tsx  // static segments
β   β   βββ index.tsx       // index of the "/blog" directory
β   βββ $.tsx               // splat route (catch-all routes)
β   βββ blog.authors.tsx    // dot delimiter
β   βββ blog.tsx            // layout for a regular route
β   βββ index.tsx
βββ root.tsx


// app/routes/posts/index.tsx
var posts_exports = {};
__export(posts_exports, {
  default: () => Posts,
  loader: () => loader5
});
var import_node7 = require("@remix-run/node"),
    import_react7 = require("@remix-run/react");
var import_jsx_dev_runtime7 = require("react/jsx-dev-runtime"),
    loader5 = async () => (0, import_node7.json)({ posts: await getPosts() });
function Posts() {
  let { posts } = (0, import_react7.useLoaderData)();
  return /* @__PURE__ */ (0, import_jsx_dev_runtime7.jsxDEV)("main", { children: [
    /* @__PURE__ */ (0, import_jsx_dev_runtime7.jsxDEV)("h1", { children: "Posts" }, void 0, !1, {
      fileName: "app/routes/posts/index.tsx"
    }, this),
    /* @__PURE__ */ (0, import_jsx_dev_runtime7.jsxDEV)(import_react7.Link, { to: "admin", ...
    }, this),
    /* @__PURE__ */ (0, import_jsx_dev_runtime7.jsxDEV)("ul", { children: posts.map((post) => ...
    }, this) }, post.slug, !1, {
      fileName: "app/routes/posts/index.tsx"
    ...
}
DIY Letβs do Remix today with own hands!


import { useParams } from "react-router";
type useQueryType = { <T>(x: string): { data: T | null } };
const useQuery: useQueryType = (type) => ({ data: null });
type SalesType = { id: number; overdue: string; dueSoon: string };
type InvoiceType = { id: number; user: string; value: string; details: string };
// URL: /sales/invoices/1
export const InvoicesPage = () => {
  return (
    <div>
      <Invoices />
    </div>
  );
};
const Invoices = () => {
  const { data: invoices } = useQuery<InvoiceType[]>("invoices");
  if (!invoices) return null;
  const { invoiceId } = useParams();
  return (
    <>
        <ul>
          {invoices.map((invoice: InvoiceType) => (
            <li key={invoice.id}>
              <a href={`/invoices/${invoice.id}`}>{invoice.user} {invoice.value}</a>
            </li>
          ))}
        </ul>
        {invoiceId && <Invoice id={Number(invoiceId)} />}
    </>
  );
};
type InvoiceProps = { id: number };
const Invoice = ({ id }: InvoiceProps) => {
  const { data: invoice } = useQuery<InvoiceType>(`invoices/${id}`);
  if (!invoice) return null;
  return (
    <div>
      {invoice.user}: {invoice.details}
    </div>
  );
};

Please share your feedback on the workshop. Thank you and have a great coding!
If you like the workshop, you can become our patron, yay! π