diff --git a/antd-style/.eslintrc.cjs b/antd-style/.eslintrc.cjs
new file mode 100644
index 00000000..4f6f59ee
--- /dev/null
+++ b/antd-style/.eslintrc.cjs
@@ -0,0 +1,84 @@
+/**
+ * This is intended to be a basic starting point for linting in your app.
+ * It relies on recommended configs out of the box for simplicity, but you can
+ * and should modify this configuration to best suit your team's needs.
+ */
+
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ root: true,
+ parserOptions: {
+ ecmaVersion: "latest",
+ sourceType: "module",
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ env: {
+ browser: true,
+ commonjs: true,
+ es6: true,
+ },
+ ignorePatterns: ["!**/.server", "!**/.client"],
+
+ // Base config
+ extends: ["eslint:recommended"],
+
+ overrides: [
+ // React
+ {
+ files: ["**/*.{js,jsx,ts,tsx}"],
+ plugins: ["react", "jsx-a11y"],
+ extends: [
+ "plugin:react/recommended",
+ "plugin:react/jsx-runtime",
+ "plugin:react-hooks/recommended",
+ "plugin:jsx-a11y/recommended",
+ ],
+ settings: {
+ react: {
+ version: "detect",
+ },
+ formComponents: ["Form"],
+ linkComponents: [
+ { name: "Link", linkAttribute: "to" },
+ { name: "NavLink", linkAttribute: "to" },
+ ],
+ "import/resolver": {
+ typescript: {},
+ },
+ },
+ },
+
+ // Typescript
+ {
+ files: ["**/*.{ts,tsx}"],
+ plugins: ["@typescript-eslint", "import"],
+ parser: "@typescript-eslint/parser",
+ settings: {
+ "import/internal-regex": "^~/",
+ "import/resolver": {
+ node: {
+ extensions: [".ts", ".tsx"],
+ },
+ typescript: {
+ alwaysTryTypes: true,
+ },
+ },
+ },
+ extends: [
+ "plugin:@typescript-eslint/recommended",
+ "plugin:import/recommended",
+ "plugin:import/typescript",
+ ],
+ },
+
+ // Node
+ {
+ files: [".eslintrc.cjs"],
+ env: {
+ node: true,
+ },
+ },
+ ],
+};
diff --git a/antd-style/.gitignore b/antd-style/.gitignore
new file mode 100644
index 00000000..80ec311f
--- /dev/null
+++ b/antd-style/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+
+/.cache
+/build
+.env
diff --git a/antd-style/README.md b/antd-style/README.md
new file mode 100644
index 00000000..284d2142
--- /dev/null
+++ b/antd-style/README.md
@@ -0,0 +1,20 @@
+# Ant Design Example (antd-style)
+
+Using [antd-style](https://github.com/ant-design/antd-style) to extract styles and inject them into the HTML can avoid page flicker.
+
+## Preview
+
+Open this example on [CodeSandbox](https://codesandbox.com):
+
+[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/remix-run/examples/tree/main/antd-style)
+
+## Example
+
+This example shows how to use Ant Design with Remix.
+
+Check the content of [./app/routes/_index.tsx](./app/routes/_index.tsx) and use `` to implement local theme changes.
+
+## Related Links
+
+[Ant Design](https://ant.design)
+[antd-style](https://github.com/ant-design/antd-style)
diff --git a/antd-style/app/entry.server.tsx b/antd-style/app/entry.server.tsx
new file mode 100644
index 00000000..5e61f442
--- /dev/null
+++ b/antd-style/app/entry.server.tsx
@@ -0,0 +1,37 @@
+import { extractStaticStyle, StyleProvider } from "antd-style";
+import { createCache } from '@ant-design/cssinjs';
+import type { EntryContext } from "@remix-run/node";
+import { RemixServer } from "@remix-run/react";
+import { renderToString } from "react-dom/server";
+
+
+export default function handleRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext,
+) {
+
+ const antdCache = createCache();
+
+ let html = renderToString(
+
+
+
+ )
+
+ const styles = extractStaticStyle(html, { antdCache })
+ .map((item) => item.tag);
+
+ html = html.replace("__ANTD__STYLES__", styles.join("\n"));
+
+ responseHeaders.set("Content-Type", "text/html");
+
+ return new Response(`${html}`, {
+ status: responseStatusCode,
+ headers: responseHeaders,
+ });
+}
diff --git a/antd-style/app/root.tsx b/antd-style/app/root.tsx
new file mode 100644
index 00000000..792cec99
--- /dev/null
+++ b/antd-style/app/root.tsx
@@ -0,0 +1,30 @@
+import {
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+} from "@remix-run/react";
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+ {typeof document === "undefined" ? "__ANTD__STYLES__" : null}
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+export default function App() {
+ return ;
+}
diff --git a/antd-style/app/routes/_index.tsx b/antd-style/app/routes/_index.tsx
new file mode 100644
index 00000000..410a26a5
--- /dev/null
+++ b/antd-style/app/routes/_index.tsx
@@ -0,0 +1,64 @@
+import { Link } from '@remix-run/react';
+import { Button, Flex, ConfigProvider } from 'antd';
+import type { ThemeConfig } from "antd";
+
+const links = [
+ {
+ href: "https://remix.run/docs",
+ text: "Remix Docs",
+ },
+ {
+ href: "https://github.com/ant-design/ant-design",
+ text: "Ant Design Repository"
+ },
+ {
+ href: "https://ant.design/",
+ text: "Ant Design Docs",
+ },
+ {
+ href: "https://github.com/ant-design/antd-style",
+ text: "antd-style Repository"
+ },
+];
+
+const theme: ThemeConfig = {
+ token: {
+ fontSize: 14,
+ colorPrimary: "#10b981",
+ },
+ components: {
+ Button: {
+ fontWeight: 400,
+ },
+ },
+};
+
+const Demo = () => (
+
+
+
+
+)
+
+export default function Index() {
+ return (
+
+
+
+
+
+
+
+
+ {
+ links.map(({ text, ...rest }) => (
+
+ ))
+ }
+ /About
+
+
+ );
+}
diff --git a/antd-style/app/routes/about.tsx b/antd-style/app/routes/about.tsx
new file mode 100644
index 00000000..4cf1e454
--- /dev/null
+++ b/antd-style/app/routes/about.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Alert } from 'antd';
+import { createStyles } from 'antd-style';
+
+const useStyle = createStyles(({ token, css }) => ({
+ title: css({
+ display: 'inline-block',
+ fontSize: token.fontSizeLG * 2,
+ background: `linear-gradient(to right, ${token.colorError}, ${token.colorWarning})`,
+ WebkitBackgroundClip: 'text',
+ WebkitTextFillColor: 'transparent',
+ })
+}))
+
+const Title = (props: React.HTMLAttributes) => (
+
+ ANTD-STYLE
+
+)
+
+const App: React.FC = () => {
+ const { styles } = useStyle();
+
+ return (
+ }
+ type="info"
+ description="css-in-js library with antd v5 token system"
+ />
+ )
+}
+
+export default App;
\ No newline at end of file
diff --git a/antd-style/package.json b/antd-style/package.json
new file mode 100644
index 00000000..a41c4f3b
--- /dev/null
+++ b/antd-style/package.json
@@ -0,0 +1,42 @@
+{
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "build": "remix vite:build",
+ "dev": "remix vite:dev",
+ "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
+ "start": "remix-serve ./build/server/index.js",
+ "typecheck": "tsc"
+ },
+ "dependencies": {
+ "@ant-design/cssinjs": "^1.22.0",
+ "@remix-run/node": "^2.9.2",
+ "@remix-run/react": "^2.9.2",
+ "@remix-run/serve": "^2.9.2",
+ "antd": "^5.22.1",
+ "antd-style": "^3.7.1",
+ "isbot": "^4.1.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@remix-run/dev": "^2.9.2",
+ "@types/react": "^18.2.20",
+ "@types/react-dom": "^18.2.7",
+ "@typescript-eslint/eslint-plugin": "^6.7.4",
+ "@typescript-eslint/parser": "^6.7.4",
+ "eslint": "^8.38.0",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.28.1",
+ "eslint-plugin-jsx-a11y": "^6.7.1",
+ "eslint-plugin-react": "^7.33.2",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "typescript": "^5.1.6",
+ "vite": "^5.1.0",
+ "vite-tsconfig-paths": "^4.2.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+}
diff --git a/antd-style/public/favicon.ico b/antd-style/public/favicon.ico
new file mode 100644
index 00000000..8830cf68
Binary files /dev/null and b/antd-style/public/favicon.ico differ
diff --git a/antd-style/sandbox.config.json b/antd-style/sandbox.config.json
new file mode 100644
index 00000000..f92e0250
--- /dev/null
+++ b/antd-style/sandbox.config.json
@@ -0,0 +1,7 @@
+{
+ "hardReloadOnChange": true,
+ "template": "remix",
+ "container": {
+ "port": 3000
+ }
+}
diff --git a/antd-style/tsconfig.json b/antd-style/tsconfig.json
new file mode 100644
index 00000000..9d87dd37
--- /dev/null
+++ b/antd-style/tsconfig.json
@@ -0,0 +1,32 @@
+{
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ "**/.server/**/*.ts",
+ "**/.server/**/*.tsx",
+ "**/.client/**/*.ts",
+ "**/.client/**/*.tsx"
+ ],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "types": ["@remix-run/node", "vite/client"],
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "target": "ES2022",
+ "strict": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+
+ // Vite takes care of building everything, not tsc.
+ "noEmit": true
+ }
+}
diff --git a/antd-style/vite.config.ts b/antd-style/vite.config.ts
new file mode 100644
index 00000000..54066fb7
--- /dev/null
+++ b/antd-style/vite.config.ts
@@ -0,0 +1,16 @@
+import { vitePlugin as remix } from "@remix-run/dev";
+import { defineConfig } from "vite";
+import tsconfigPaths from "vite-tsconfig-paths";
+
+export default defineConfig({
+ plugins: [
+ remix({
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ },
+ }),
+ tsconfigPaths(),
+ ],
+});