diff --git a/.gitignore b/.gitignore
index 150de1370..d7fd20052 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,4 +32,4 @@ yarn-error.log*
.fleet
# Fumadocs build (TEMPORARY)
-fumadocs
+# fumadocs
diff --git a/convert-themed-images.js b/convert-themed-images.js
new file mode 100755
index 000000000..117e88b08
--- /dev/null
+++ b/convert-themed-images.js
@@ -0,0 +1,121 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const path = require('path');
+
+/**
+ * Converts ThemedImage components to standard markdown image syntax
+ * @param {string} content - The file content to process
+ * @returns {string} - The converted content
+ */
+function convertThemedImages(content) {
+ // Regex to match ThemedImage components with optional p tags
+ const themedImageRegex =
+ /
\s* \s*<\/p>/g;
+
+ // Also match without p tags
+ const themedImageNoPTagRegex =
+ / /g;
+
+ // Convert ThemedImage components with p tags
+ let convertedContent = content.replace(
+ themedImageRegex,
+ (match, altText, lightSource, darkSource) => {
+ return ``;
+ },
+ );
+
+ // Convert ThemedImage components without p tags
+ convertedContent = convertedContent.replace(
+ themedImageNoPTagRegex,
+ (match, altText, lightSource, darkSource) => {
+ return ``;
+ },
+ );
+
+ return convertedContent;
+}
+
+/**
+ * Process a single file
+ * @param {string} filePath - Path to the file to process
+ */
+function processFile(filePath) {
+ try {
+ const content = fs.readFileSync(filePath, 'utf8');
+ const convertedContent = convertThemedImages(content);
+
+ if (content !== convertedContent) {
+ fs.writeFileSync(filePath, convertedContent, 'utf8');
+ console.log(`✅ Converted: ${filePath}`);
+ } else {
+ console.log(`⏭️ No changes needed: ${filePath}`);
+ }
+ } catch (error) {
+ console.error(`❌ Error processing ${filePath}:`, error.message);
+ }
+}
+
+/**
+ * Recursively process all .mdx files in a directory
+ * @param {string} dirPath - Directory path to process
+ */
+function processDirectory(dirPath) {
+ try {
+ const items = fs.readdirSync(dirPath);
+
+ for (const item of items) {
+ const fullPath = path.join(dirPath, item);
+ const stat = fs.statSync(fullPath);
+
+ if (stat.isDirectory()) {
+ processDirectory(fullPath);
+ } else if (item.endsWith('.mdx')) {
+ processFile(fullPath);
+ }
+ }
+ } catch (error) {
+ console.error(`❌ Error processing directory ${dirPath}:`, error.message);
+ }
+}
+
+// Main execution
+function main() {
+ const args = process.argv.slice(2);
+
+ if (args.length === 0) {
+ console.log('Usage: node convert-themed-images.js ');
+ console.log('Examples:');
+ console.log(
+ ' node convert-themed-images.js fumadocs/content/blog/2023-10-02-what-is-a-state-machine/index.mdx',
+ );
+ console.log(' node convert-themed-images.js fumadocs/content/blog/');
+ process.exit(1);
+ }
+
+ const targetPath = args[0];
+
+ if (!fs.existsSync(targetPath)) {
+ console.error(`❌ Path does not exist: ${targetPath}`);
+ process.exit(1);
+ }
+
+ const stat = fs.statSync(targetPath);
+
+ if (stat.isFile()) {
+ if (targetPath.endsWith('.mdx')) {
+ processFile(targetPath);
+ } else {
+ console.error('❌ File must be a .mdx file');
+ process.exit(1);
+ }
+ } else if (stat.isDirectory()) {
+ console.log(`🔄 Processing directory: ${targetPath}`);
+ processDirectory(targetPath);
+ }
+
+ console.log('✨ Conversion complete!');
+}
+
+// Run the script
+main();
diff --git a/fumadocs/.gitignore b/fumadocs/.gitignore
new file mode 100644
index 000000000..55a12ae71
--- /dev/null
+++ b/fumadocs/.gitignore
@@ -0,0 +1,28 @@
+# deps
+/node_modules
+
+# generated content
+.contentlayer
+.content-collections
+.source
+
+# test & build
+/coverage
+/.next/
+/out/
+/build
+*.tsbuildinfo
+
+# misc
+.DS_Store
+*.pem
+/.pnp
+.pnp.js
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# others
+.env*.local
+.vercel
+next-env.d.ts
\ No newline at end of file
diff --git a/fumadocs/README.md b/fumadocs/README.md
new file mode 100644
index 000000000..70fd7dfd2
--- /dev/null
+++ b/fumadocs/README.md
@@ -0,0 +1,45 @@
+# fumadocs
+
+This is a Next.js application generated with
+[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
+
+Run development server:
+
+```bash
+npm run dev
+# or
+pnpm dev
+# or
+yarn dev
+```
+
+Open http://localhost:3000 with your browser to see the result.
+
+## Explore
+
+In the project, you can see:
+
+- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
+- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.
+
+| Route | Description |
+| ------------------------- | ------------------------------------------------------ |
+| `app/(home)` | The route group for your landing page and other pages. |
+| `app/docs` | The documentation layout and pages. |
+| `app/api/search/route.ts` | The Route Handler for search. |
+
+### Fumadocs MDX
+
+A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
+
+Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
+
+## Learn More
+
+To learn more about Next.js and Fumadocs, take a look at the following
+resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
+ features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs
diff --git a/fumadocs/app/(home)/layout.tsx b/fumadocs/app/(home)/layout.tsx
new file mode 100644
index 000000000..b8b7d1b90
--- /dev/null
+++ b/fumadocs/app/(home)/layout.tsx
@@ -0,0 +1,77 @@
+import { baseOptions } from '@/lib/layout.shared';
+import type { ReactNode } from 'react';
+import { HomeLayout } from 'fumadocs-ui/layouts/home';
+import { TelescopeIcon } from 'lucide-react';
+
+export default function Layout({ children }: { children: ReactNode }) {
+ return (
+ ,
+ },
+ {
+ text: 'Blog',
+ url: '/blog',
+ active: 'nested-url',
+ },
+ {
+ type: 'icon',
+ text: 'Visit XState GitHub repository',
+ icon: (
+
+
+
+
+ ),
+ url: 'https://github.com/statelyai/xstate',
+ external: true,
+ },
+ ]}
+ >
+ {children}
+
+ );
+}
diff --git a/fumadocs/app/(home)/page.tsx b/fumadocs/app/(home)/page.tsx
new file mode 100644
index 000000000..4393163a5
--- /dev/null
+++ b/fumadocs/app/(home)/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function HomePage() {
+ redirect('/docs');
+}
diff --git a/fumadocs/app/api/chat/route.ts b/fumadocs/app/api/chat/route.ts
new file mode 100644
index 000000000..9952728f6
--- /dev/null
+++ b/fumadocs/app/api/chat/route.ts
@@ -0,0 +1,30 @@
+import { ProvideLinksToolSchema } from '../../../lib/inkeep-qa-schema';
+import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
+import { convertToModelMessages, streamText } from 'ai';
+
+export const runtime = 'edge';
+
+const openai = createOpenAICompatible({
+ name: 'inkeep',
+ apiKey: process.env.INKEEP_API_KEY,
+ baseURL: 'https://api.inkeep.com/v1',
+});
+
+export async function POST(req: Request) {
+ const reqJson = await req.json();
+
+ const result = streamText({
+ model: openai('inkeep-qa-sonnet-4'),
+ tools: {
+ provideLinks: {
+ inputSchema: ProvideLinksToolSchema,
+ },
+ },
+ messages: convertToModelMessages(reqJson.messages, {
+ ignoreIncompleteToolCalls: true,
+ }),
+ toolChoice: 'auto',
+ });
+
+ return result.toUIMessageStreamResponse();
+}
diff --git a/fumadocs/app/api/search/route.ts b/fumadocs/app/api/search/route.ts
new file mode 100644
index 000000000..7ba7e8231
--- /dev/null
+++ b/fumadocs/app/api/search/route.ts
@@ -0,0 +1,7 @@
+import { source } from '@/lib/source';
+import { createFromSource } from 'fumadocs-core/search/server';
+
+export const { GET } = createFromSource(source, {
+ // https://docs.orama.com/docs/orama-js/supported-languages
+ language: 'english',
+});
diff --git a/fumadocs/app/blog/[slug]/page.client.tsx b/fumadocs/app/blog/[slug]/page.client.tsx
new file mode 100644
index 000000000..d88f56be3
--- /dev/null
+++ b/fumadocs/app/blog/[slug]/page.client.tsx
@@ -0,0 +1,24 @@
+'use client';
+import { Check, Share } from 'lucide-react';
+import { cn } from '@/lib/cn';
+import { buttonVariants } from '@/components/ui/button';
+import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
+
+export function Control({ url }: { url: string }) {
+ const [isChecked, onCopy] = useCopyButton(() => {
+ void navigator.clipboard.writeText(`${window.location.origin}${url}`);
+ });
+
+ return (
+
+ {isChecked ? : }
+ {isChecked ? 'Copied URL' : 'Share Post'}
+
+ );
+}
diff --git a/fumadocs/app/blog/[slug]/page.tsx b/fumadocs/app/blog/[slug]/page.tsx
new file mode 100644
index 000000000..1a3fcb702
--- /dev/null
+++ b/fumadocs/app/blog/[slug]/page.tsx
@@ -0,0 +1,88 @@
+import type { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+import Link from 'next/link';
+import { InlineTOC } from 'fumadocs-ui/components/inline-toc';
+import { blog } from '@/lib/source';
+import { buttonVariants } from '@/components/ui/button';
+import { Control } from '@/app/blog/[slug]/page.client';
+import { getMDXComponents } from '@/mdx-components';
+import path from 'node:path';
+
+export default async function Page(props: PageProps<'/blog/[slug]'>) {
+ const params = await props.params;
+ const page = blog.getPage([params.slug]);
+
+ if (!page) notFound();
+ const { body: Mdx, toc } = page.data;
+
+ return (
+ <>
+
+
+ {page.data.title}
+
+
{page.data.description}
+
+ Back
+
+
+
+
+
+
+
+
+
+
Written by
+
{page.data.authors.join(', ')}
+
+
+
At
+
+ {new Date(
+ page.data.date ??
+ path.basename(page.path, path.extname(page.path)),
+ ).toDateString()}
+
+
+
+
+
+ >
+ );
+}
+
+export async function generateMetadata(
+ props: PageProps<'/blog/[slug]'>,
+): Promise {
+ const params = await props.params;
+ const page = blog.getPage([params.slug]);
+
+ if (!page) notFound();
+
+ return {
+ title: page.data.title,
+ description:
+ page.data.description ?? 'The library for building documentation sites',
+ };
+}
+
+export function generateStaticParams(): { slug: string }[] {
+ return blog.getPages().map((page) => ({
+ slug: page.slugs[0],
+ }));
+}
diff --git a/fumadocs/app/blog/layout.tsx b/fumadocs/app/blog/layout.tsx
new file mode 100644
index 000000000..8e9ec7226
--- /dev/null
+++ b/fumadocs/app/blog/layout.tsx
@@ -0,0 +1,11 @@
+import { DocsLayout } from 'fumadocs-ui/layouts/docs';
+import { baseOptions } from '@/lib/layout.shared';
+import { source } from '@/lib/source';
+
+export default function Layout({ children }: LayoutProps<'/docs'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/fumadocs/app/blog/page.tsx b/fumadocs/app/blog/page.tsx
new file mode 100644
index 000000000..3cba584c3
--- /dev/null
+++ b/fumadocs/app/blog/page.tsx
@@ -0,0 +1,64 @@
+import Link from 'next/link';
+import { blog } from '@/lib/source';
+
+export default function Page() {
+ const posts = [...blog.getPages()].sort(
+ (a, b) =>
+ new Date(b.data.date ?? b.file.name).getTime() -
+ new Date(a.data.date ?? a.file.name).getTime(),
+ );
+
+ const svg = `
+
+
+
+
+
+ `;
+
+ return (
+
+
+
+ Stately Blog
+
+
+ The latest news and updates from the Stately team
+
+
+
+ {posts.map((post) => (
+
+
{post.data.title}
+
+ {post.data.description}
+
+
+
+ {new Date(post.data.date ?? post.file.name).toDateString()}
+
+
+ ))}
+
+
+ );
+}
diff --git a/fumadocs/app/docs/[[...slug]]/page.tsx b/fumadocs/app/docs/[[...slug]]/page.tsx
new file mode 100644
index 000000000..357971e55
--- /dev/null
+++ b/fumadocs/app/docs/[[...slug]]/page.tsx
@@ -0,0 +1,62 @@
+import { getPageImage, source } from '@/lib/source';
+import {
+ DocsBody,
+ DocsDescription,
+ DocsPage,
+ DocsTitle,
+} from 'fumadocs-ui/page';
+import { notFound } from 'next/navigation';
+import { getMDXComponents } from '@/mdx-components';
+import type { Metadata } from 'next';
+import { createRelativeLink } from 'fumadocs-ui/mdx';
+import { LLMCopyButton, ViewOptions } from '@/components/page-actions';
+
+export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
+ const params = await props.params;
+ const page = source.getPage(params.slug);
+ if (!page) notFound();
+
+ const MDX = page.data.body;
+
+ return (
+
+ {page.data.title}
+ {page.data.description}
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function generateStaticParams() {
+ return source.generateParams();
+}
+
+export async function generateMetadata(
+ props: PageProps<'/docs/[[...slug]]'>,
+): Promise {
+ const params = await props.params;
+ const page = source.getPage(params.slug);
+ if (!page) notFound();
+
+ return {
+ title: page.data.title,
+ description: page.data.description,
+ openGraph: {
+ images: getPageImage(page).url,
+ },
+ };
+}
diff --git a/fumadocs/app/docs/layout.tsx b/fumadocs/app/docs/layout.tsx
new file mode 100644
index 000000000..bac1433aa
--- /dev/null
+++ b/fumadocs/app/docs/layout.tsx
@@ -0,0 +1,576 @@
+import { DocsLayout } from 'fumadocs-ui/layouts/docs';
+import { baseOptions } from '@/lib/layout.shared';
+
+export default function Layout({ children }: LayoutProps<'/docs'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/fumadocs/app/global.css b/fumadocs/app/global.css
new file mode 100644
index 000000000..b6b46defd
--- /dev/null
+++ b/fumadocs/app/global.css
@@ -0,0 +1,85 @@
+@import 'tailwindcss';
+@import 'fumadocs-ui/css/neutral.css';
+@import 'fumadocs-ui/css/preset.css';
+@import 'fumadocs-twoslash/twoslash.css';
+
+@theme {
+ /* Light mode - matching original custom.css colors */
+ --color-fd-background: white;
+ --color-fd-foreground: hsl(240, 4.3%, 17.6%); /* #2d2e34 - st-gray-800 */
+ --color-fd-muted: hsl(240, 5.9%, 94.1%); /* #efeff1 - st-gray-50 */
+ --color-fd-muted-foreground: hsl(
+ 240,
+ 3.8%,
+ 40.4%
+ ); /* #5d5f6a - st-gray-600 */
+ --color-fd-popover: white;
+ --color-fd-popover-foreground: hsl(
+ 240,
+ 4.3%,
+ 17.6%
+ ); /* #2d2e34 - st-gray-800 */
+ --color-fd-card: hsl(240, 5.9%, 94.1%); /* #efeff1 - st-gray-50 */
+ --color-fd-card-foreground: hsl(240, 4.3%, 17.6%); /* #2d2e34 - st-gray-800 */
+ --color-fd-border: hsl(240, 5.9%, 88.2%); /* #e1e2e5 - st-gray-100 */
+ --color-fd-primary: hsl(240, 5.1%, 8.2%); /* #15151d - st-gray-900 */
+ --color-fd-primary-foreground: white;
+ --color-fd-secondary: hsl(240, 5.9%, 94.1%); /* #efeff1 - st-gray-50 */
+ --color-fd-secondary-foreground: hsl(
+ 240,
+ 4.3%,
+ 17.6%
+ ); /* #2d2e34 - st-gray-800 */
+ --color-fd-accent: hsl(240, 5.9%, 88.2%); /* #e1e2e5 - st-gray-100 */
+ --color-fd-accent-foreground: hsl(
+ 240,
+ 4.3%,
+ 17.6%
+ ); /* #2d2e34 - st-gray-800 */
+ --color-fd-ring: hsl(240, 3.7%, 46.1%); /* #757785 - st-gray-500 */
+}
+.dark {
+ /* Dark mode - matching original custom.css colors */
+ --color-fd-background: #15151d; /* #15151d - st-gray-900 */
+ --color-fd-foreground: hsl(240, 4.9%, 78.8%); /* #c6c7cd - st-gray-200 */
+ --color-fd-muted: hsl(240, 4.3%, 17.6%); /* #2d2e34 - st-gray-800 */
+ --color-fd-muted-foreground: hsl(
+ 240,
+ 3.7%,
+ 58.2%
+ ); /* #8f919d - st-gray-400 */
+ --color-fd-popover: hsl(240, 4.3%, 17.6%); /* #2d2e34 - st-gray-800 */
+ --color-fd-popover-foreground: hsl(
+ 240,
+ 4.9%,
+ 78.8%
+ ); /* #c6c7cd - st-gray-200 */
+ --color-fd-card: #15151d; /* #2d2e34 - st-gray-800 */
+ --color-fd-card-foreground: hsl(240, 5.9%, 96.1%); /* #efeff1 - st-gray-50 */
+ --color-fd-border: hsl(240, 3.4%, 28.6%); /* #45464f - st-gray-700 */
+ --color-fd-primary: hsl(240, 5.9%, 96.1%); /* #efeff1 - st-gray-50 */
+ --color-fd-primary-foreground: hsl(
+ 240,
+ 5.1%,
+ 8.2%
+ ); /* #15151d - st-gray-900 */
+ --color-fd-secondary: hsl(240, 4.3%, 17.6%); /* #2d2e34 - st-gray-800 */
+ --color-fd-secondary-foreground: hsl(
+ 240,
+ 4.9%,
+ 78.8%
+ ); /* #c6c7cd - st-gray-200 */
+ --color-fd-accent: hsl(240, 3.4%, 28.6%); /* #45464f - st-gray-700 */
+ --color-fd-accent-foreground: hsl(
+ 240,
+ 5.9%,
+ 96.1%
+ ); /* #efeff1 - st-gray-50 */
+ --color-fd-ring: hsl(240, 3.7%, 46.1%); /* #757785 - st-gray-500 */
+}
+
+.prose {
+ svg {
+ display: inline-block;
+ }
+}
diff --git a/fumadocs/app/layout.tsx b/fumadocs/app/layout.tsx
new file mode 100644
index 000000000..0b9d76415
--- /dev/null
+++ b/fumadocs/app/layout.tsx
@@ -0,0 +1,17 @@
+import '@/app/global.css';
+import { RootProvider } from 'fumadocs-ui/provider/next';
+import { Inter } from 'next/font/google';
+
+const inter = Inter({
+ subsets: ['latin'],
+});
+
+export default function Layout({ children }: LayoutProps<'/'>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/fumadocs/app/llms-full.txt/route.ts b/fumadocs/app/llms-full.txt/route.ts
new file mode 100644
index 000000000..d494d2cbb
--- /dev/null
+++ b/fumadocs/app/llms-full.txt/route.ts
@@ -0,0 +1,10 @@
+import { getLLMText, source } from '@/lib/source';
+
+export const revalidate = false;
+
+export async function GET() {
+ const scan = source.getPages().map(getLLMText);
+ const scanned = await Promise.all(scan);
+
+ return new Response(scanned.join('\n\n'));
+}
diff --git a/fumadocs/app/llms.mdx/[[...slug]]/route.ts b/fumadocs/app/llms.mdx/[[...slug]]/route.ts
new file mode 100644
index 000000000..c029b0f44
--- /dev/null
+++ b/fumadocs/app/llms.mdx/[[...slug]]/route.ts
@@ -0,0 +1,24 @@
+import { getLLMText } from '@/lib/get-llm-text';
+import { source } from '@/lib/source';
+import { notFound } from 'next/navigation';
+
+export const revalidate = false;
+
+export async function GET(
+ _req: Request,
+ { params }: RouteContext<'/llms.mdx/[[...slug]]'>,
+) {
+ const { slug } = await params;
+ const page = source.getPage(slug);
+ if (!page) notFound();
+
+ return new Response(await getLLMText(page), {
+ headers: {
+ 'Content-Type': 'text/markdown',
+ },
+ });
+}
+
+export function generateStaticParams() {
+ return source.generateParams();
+}
diff --git a/fumadocs/app/middleware.ts b/fumadocs/app/middleware.ts
new file mode 100644
index 000000000..826a697b0
--- /dev/null
+++ b/fumadocs/app/middleware.ts
@@ -0,0 +1,12 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { isMarkdownPreferred, rewritePath } from 'fumadocs-core/negotiation';
+const { rewrite: rewriteLLM } = rewritePath('/docs/*path', '/llms.mdx/*path');
+export function middleware(request: NextRequest) {
+ if (isMarkdownPreferred(request)) {
+ const result = rewriteLLM(request.nextUrl.pathname);
+ if (result) {
+ return NextResponse.rewrite(new URL(result, request.nextUrl));
+ }
+ }
+ return NextResponse.next();
+}
diff --git a/fumadocs/app/og/docs/[...slug]/route.tsx b/fumadocs/app/og/docs/[...slug]/route.tsx
new file mode 100644
index 000000000..f5df96dbc
--- /dev/null
+++ b/fumadocs/app/og/docs/[...slug]/route.tsx
@@ -0,0 +1,36 @@
+import { getPageImage, source } from '@/lib/source';
+import { notFound } from 'next/navigation';
+import { ImageResponse } from 'next/og';
+import { generate as DefaultImage } from 'fumadocs-ui/og';
+
+export const revalidate = false;
+
+export async function GET(
+ _req: Request,
+ { params }: RouteContext<'/og/docs/[...slug]'>,
+) {
+ const { slug } = await params;
+ const page = source.getPage(slug.slice(0, -1));
+ if (!page) notFound();
+
+ return new ImageResponse(
+ (
+
+ ),
+ {
+ width: 1200,
+ height: 630,
+ },
+ );
+}
+
+export function generateStaticParams() {
+ return source.getPages().map((page) => ({
+ lang: page.locale,
+ slug: getPageImage(page).segments,
+ }));
+}
diff --git a/fumadocs/cli.json b/fumadocs/cli.json
new file mode 100644
index 000000000..15844394d
--- /dev/null
+++ b/fumadocs/cli.json
@@ -0,0 +1,11 @@
+{
+ "aliases": {
+ "uiDir": "./components/ui",
+ "componentsDir": "./components",
+ "blockDir": "./components",
+ "cssDir": "./styles",
+ "libDir": "./lib"
+ },
+ "baseDir": "",
+ "commands": {}
+}
\ No newline at end of file
diff --git a/fumadocs/components/markdown.tsx b/fumadocs/components/markdown.tsx
new file mode 100644
index 000000000..7c2cddc3d
--- /dev/null
+++ b/fumadocs/components/markdown.tsx
@@ -0,0 +1,119 @@
+import { remark } from 'remark';
+import remarkGfm from 'remark-gfm';
+import remarkRehype from 'remark-rehype';
+import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
+import {
+ Children,
+ type ComponentProps,
+ type ReactElement,
+ type ReactNode,
+ Suspense,
+ use,
+ useDeferredValue,
+} from 'react';
+import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
+import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
+import defaultMdxComponents from 'fumadocs-ui/mdx';
+import { visit } from 'unist-util-visit';
+import type { ElementContent, Root, RootContent } from 'hast';
+
+export interface Processor {
+ process: (content: string) => Promise;
+}
+
+export function rehypeWrapWords() {
+ return (tree: Root) => {
+ visit(tree, ['text', 'element'], (node, index, parent) => {
+ if (node.type === 'element' && node.tagName === 'pre') return 'skip';
+ if (node.type !== 'text' || !parent || index === undefined) return;
+
+ const words = node.value.split(/(?=\s)/);
+
+ // Create new span nodes for each word and whitespace
+ const newNodes: ElementContent[] = words.flatMap((word) => {
+ if (word.length === 0) return [];
+
+ return {
+ type: 'element',
+ tagName: 'span',
+ properties: {
+ class: 'animate-fd-fade-in',
+ },
+ children: [{ type: 'text', value: word }],
+ };
+ });
+
+ Object.assign(node, {
+ type: 'element',
+ tagName: 'span',
+ properties: {},
+ children: newNodes,
+ } satisfies RootContent);
+ return 'skip';
+ });
+ };
+}
+
+function createProcessor(): Processor {
+ const processor = remark()
+ .use(remarkGfm)
+ .use(remarkRehype)
+ .use(rehypeWrapWords);
+
+ return {
+ async process(content) {
+ const nodes = processor.parse({ value: content });
+ const hast = await processor.run(nodes);
+
+ return toJsxRuntime(hast, {
+ development: false,
+ jsx,
+ jsxs,
+ Fragment,
+ components: {
+ ...defaultMdxComponents,
+ pre: Pre,
+ img: undefined, // use JSX
+ },
+ });
+ },
+ };
+}
+
+function Pre(props: ComponentProps<'pre'>) {
+ const code = Children.only(props.children) as ReactElement;
+ const codeProps = code.props as ComponentProps<'code'>;
+ const content = codeProps.children;
+ if (typeof content !== 'string') return null;
+
+ let lang =
+ codeProps.className
+ ?.split(' ')
+ .find((v) => v.startsWith('language-'))
+ ?.slice('language-'.length) ?? 'text';
+
+ if (lang === 'mdx') lang = 'md';
+
+ return ;
+}
+
+const processor = createProcessor();
+
+export function Markdown({ text }: { text: string }) {
+ const deferredText = useDeferredValue(text);
+
+ return (
+ {text}
}>
+
+
+ );
+}
+
+const cache = new Map>();
+
+function Renderer({ text }: { text: string }) {
+ const result = cache.get(text) ?? processor.process(text);
+ cache.set(text, result);
+
+ return use(result);
+}
diff --git a/fumadocs/components/page-actions.tsx b/fumadocs/components/page-actions.tsx
new file mode 100644
index 000000000..63ccf70cd
--- /dev/null
+++ b/fumadocs/components/page-actions.tsx
@@ -0,0 +1,247 @@
+'use client';
+import { useMemo, useState } from 'react';
+import {
+ Check,
+ ChevronDown,
+ Copy,
+ ExternalLinkIcon,
+ MessageCircleIcon,
+} from 'lucide-react';
+import { cn } from '../lib/cn';
+import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
+import { buttonVariants } from './ui/button';
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from 'fumadocs-ui/components/ui/popover';
+import { cva } from 'class-variance-authority';
+
+const cache = new Map();
+
+export function LLMCopyButton({
+ /**
+ * A URL to fetch the raw Markdown/MDX content of page
+ */
+ markdownUrl,
+}: {
+ markdownUrl: string;
+}) {
+ const [isLoading, setLoading] = useState(false);
+ const [checked, onClick] = useCopyButton(async () => {
+ const cached = cache.get(markdownUrl);
+ if (cached) return navigator.clipboard.writeText(cached);
+
+ setLoading(true);
+
+ try {
+ await navigator.clipboard.write([
+ new ClipboardItem({
+ 'text/plain': fetch(markdownUrl).then(async (res) => {
+ const content = await res.text();
+ cache.set(markdownUrl, content);
+
+ return content;
+ }),
+ }),
+ ]);
+ } finally {
+ setLoading(false);
+ }
+ });
+
+ return (
+
+ {checked ? : }
+ Copy Markdown
+
+ );
+}
+
+const optionVariants = cva(
+ 'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4',
+);
+
+export function ViewOptions({
+ markdownUrl,
+ githubUrl,
+}: {
+ /**
+ * A URL to the raw Markdown/MDX content of page
+ */
+ markdownUrl: string;
+
+ /**
+ * Source file URL on GitHub
+ */
+ githubUrl: string;
+}) {
+ const items = useMemo(() => {
+ const fullMarkdownUrl =
+ typeof window !== 'undefined'
+ ? new URL(markdownUrl, window.location.origin)
+ : 'loading';
+ const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`;
+
+ return [
+ {
+ title: 'Open in GitHub',
+ href: githubUrl,
+ icon: (
+
+ GitHub
+
+
+ ),
+ },
+ {
+ title: 'Open in Scira AI',
+ href: `https://scira.ai/?${new URLSearchParams({
+ q,
+ })}`,
+ icon: (
+
+ Scira AI
+
+
+
+
+
+
+
+
+ ),
+ },
+ {
+ title: 'Open in ChatGPT',
+ href: `https://chatgpt.com/?${new URLSearchParams({
+ hints: 'search',
+ q,
+ })}`,
+ icon: (
+
+ OpenAI
+
+
+ ),
+ },
+ {
+ title: 'Open in Claude',
+ href: `https://claude.ai/new?${new URLSearchParams({
+ q,
+ })}`,
+ icon: (
+
+ Anthropic
+
+
+ ),
+ },
+ {
+ title: 'Open in T3 Chat',
+ href: `https://t3.chat/new?${new URLSearchParams({
+ q,
+ })}`,
+ icon: ,
+ },
+ ];
+ }, [githubUrl, markdownUrl]);
+
+ return (
+
+
+ Open
+
+
+
+ {items.map((item) => (
+
+ {item.icon}
+ {item.title}
+
+
+ ))}
+
+
+ );
+}
diff --git a/fumadocs/components/search.tsx b/fumadocs/components/search.tsx
new file mode 100644
index 000000000..42ab84d10
--- /dev/null
+++ b/fumadocs/components/search.tsx
@@ -0,0 +1,385 @@
+'use client';
+import { RemoveScroll } from 'react-remove-scroll';
+import {
+ type ComponentProps,
+ createContext,
+ type SyntheticEvent,
+ use,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import { Loader2, RefreshCw, SearchIcon, Send, X } from 'lucide-react';
+import { cn } from '../lib/cn';
+import { buttonVariants } from './ui/button';
+import Link from 'fumadocs-core/link';
+import { type UIMessage, useChat, type UseChatHelpers } from '@ai-sdk/react';
+import type { ProvideLinksToolSchema } from '../lib/inkeep-qa-schema';
+import type { z } from 'zod';
+import { DefaultChatTransport } from 'ai';
+import { Markdown } from './markdown';
+import { Presence } from '@radix-ui/react-presence';
+
+const Context = createContext<{
+ open: boolean;
+ setOpen: (open: boolean) => void;
+ chat: UseChatHelpers;
+} | null>(null);
+
+function useChatContext() {
+ return use(Context)!.chat;
+}
+
+function SearchAIActions() {
+ const { messages, status, setMessages, regenerate } = useChatContext();
+ const isLoading = status === 'streaming';
+
+ if (messages.length === 0) return null;
+
+ return (
+ <>
+ {!isLoading && messages.at(-1)?.role === 'assistant' && (
+ regenerate()}
+ >
+
+ Retry
+
+ )}
+ setMessages([])}
+ >
+ Clear Chat
+
+ >
+ );
+}
+
+function SearchAIInput(props: ComponentProps<'form'>) {
+ const { status, sendMessage, stop } = useChatContext();
+ const [input, setInput] = useState('');
+ const isLoading = status === 'streaming' || status === 'submitted';
+ const onStart = (e?: SyntheticEvent) => {
+ e?.preventDefault();
+ void sendMessage({ text: input });
+ setInput('');
+ };
+
+ useEffect(() => {
+ if (isLoading) document.getElementById('nd-ai-input')?.focus();
+ }, [isLoading]);
+
+ return (
+
+ );
+}
+
+function List(props: Omit, 'dir'>) {
+ const containerRef = useRef(null);
+
+ useEffect(() => {
+ if (!containerRef.current) return;
+ function callback() {
+ const container = containerRef.current;
+ if (!container) return;
+
+ container.scrollTo({
+ top: container.scrollHeight,
+ behavior: 'instant',
+ });
+ }
+
+ const observer = new ResizeObserver(callback);
+ callback();
+
+ const element = containerRef.current?.firstElementChild;
+
+ if (element) {
+ observer.observe(element);
+ }
+
+ return () => {
+ observer.disconnect();
+ };
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+}
+
+function Input(props: ComponentProps<'textarea'>) {
+ const ref = useRef(null);
+ const shared = cn('col-start-1 row-start-1', props.className);
+
+ return (
+
+
+
+ {`${props.value?.toString() ?? ''}\n`}
+
+
+ );
+}
+
+const roleName: Record = {
+ user: 'you',
+ assistant: 'fumadocs',
+};
+
+function Message({
+ message,
+ ...props
+}: { message: UIMessage } & ComponentProps<'div'>) {
+ let markdown = '';
+ let links: z.infer['links'] = [];
+
+ for (const part of message.parts ?? []) {
+ if (part.type === 'text') {
+ markdown += part.text;
+ continue;
+ }
+
+ if (part.type === 'tool-provideLinks' && part.input) {
+ links = (part.input as z.infer).links;
+ }
+ }
+
+ return (
+
+
+ {roleName[message.role] ?? 'unknown'}
+
+
+
+
+ {links && links.length > 0 ? (
+
+ {links.map((item, i) => (
+
+
{item.title}
+
Reference {item.label}
+
+ ))}
+
+ ) : null}
+
+ );
+}
+
+export function AISearchTrigger() {
+ const [open, setOpen] = useState(false);
+ const chat = useChat({
+ id: 'search',
+ transport: new DefaultChatTransport({
+ api: '/api/chat',
+ }),
+ });
+
+ const onKeyPress = (e: KeyboardEvent) => {
+ if (e.key === 'Escape' && open) {
+ setOpen(false);
+ e.preventDefault();
+ }
+
+ if (e.key === '/' && (e.metaKey || e.ctrlKey) && !open) {
+ setOpen(true);
+ e.preventDefault();
+ }
+ };
+
+ const onKeyPressRef = useRef(onKeyPress);
+ onKeyPressRef.current = onKeyPress;
+ useEffect(() => {
+ const listener = (e: KeyboardEvent) => onKeyPressRef.current(e);
+ window.addEventListener('keydown', listener);
+ return () => window.removeEventListener('keydown', listener);
+ }, []);
+
+ return (
+ ({ chat, open, setOpen }), [chat, open])}>
+
+
+ {
+ if (e.target === e.currentTarget) {
+ setOpen(false);
+ e.preventDefault();
+ }
+ }}
+ >
+
+
+ Powered by Inkeep AI
+
+
setOpen(false)}
+ >
+
+
+
+
+
+ {chat.messages
+ .filter((msg) => msg.role !== 'system')
+ .map((item) => (
+
+ ))}
+
+
+
+
+
+
+ setOpen(true)}
+ >
+
+ Ask AI
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/fumadocs/components/ui/button.tsx b/fumadocs/components/ui/button.tsx
new file mode 100644
index 000000000..b427d4e08
--- /dev/null
+++ b/fumadocs/components/ui/button.tsx
@@ -0,0 +1,28 @@
+import { cva, type VariantProps } from 'class-variance-authority';
+
+const variants = {
+ primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
+ outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
+ ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
+ secondary:
+ 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
+} as const;
+
+export const buttonVariants = cva(
+ 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-ring',
+ {
+ variants: {
+ variant: variants,
+ // fumadocs use `color` instead of `variant`
+ color: variants,
+ size: {
+ sm: 'gap-1 px-2 py-1.5 text-xs',
+ icon: 'p-1.5 [&_svg]:size-5',
+ 'icon-sm': 'p-1.5 [&_svg]:size-4.5',
+ 'icon-xs': 'p-1 [&_svg]:size-4',
+ },
+ },
+ },
+);
+
+export type ButtonProps = VariantProps;
diff --git a/fumadocs/content/blog/2019-11-13-no-disabling-a-button-is-not-app-logic/index.mdx b/fumadocs/content/blog/2019-11-13-no-disabling-a-button-is-not-app-logic/index.mdx
new file mode 100644
index 000000000..0153548e9
--- /dev/null
+++ b/fumadocs/content/blog/2019-11-13-no-disabling-a-button-is-not-app-logic/index.mdx
@@ -0,0 +1,549 @@
+---
+title: 'No, disabling a button is not app logic.'
+description: >-
+ Disabling a button is not logic. Rather, it is a sign that logic is fragile
+ and bug-prone. Let’s explore this with a simple example: fetching data in a
+ React component.
+tags: [data fetching, state machine, statechart, state, business logic]
+authors: [david]
+image: /blog/2019-11-13-no-disabling-a-button-is-not-app-logic.png
+slug: 2019-11-13-no-disabling-a-button-is-not-app-logic
+date: 2019-11-13
+---
+
+
+
+
+
+I’m going to start this post with an excerpt from the book “Constructing the User Interface with Statecharts”, written by Ian Horrocks in 1999:
+
+> User interface development tools are very powerful. They can be used to construct large and complex user interfaces, with only a relatively small amount of code written by an application developer. And yet, despite the power of such tools and the relatively small amount of code that is written, user interface software often has the following characteristics:
+>
+> - the code can be difficult to understand and review thoroughly:
+> - the code can be difficult to test in a systematic and thorough way;
+> - the code can contain bugs even after extensive testing and bug fixing;
+> - the code can be difficult to enhance without introducing unwanted side-effects;
+> - the quality of the code tends to deteriorate as enhancements are made to it.
+>
+> Despite the obvious problems associated with user interface development, **little effort has been made to improve the situation**. Any practitioner who has worked on large user interface projects will be familiar with many of the above characteristics, which are **symptomatic of the way in which the software is constructed**.
+
+{/* truncate */}
+
+In case you didn’t do the math, this was written _over 20 years ago_ and yet it echoes the same sentiments that many developers feel today about the state of app development. Why is that?
+
+We’ll explore this with a simple example: fetching data in a React component. Keep in mind, the ideas presented in this article are not library-specific, nor framework-specific… in fact, they’re not even language specific!
+
+## Trying to make `fetch()` happen
+
+Suppose we have a `DogFetcher` component that has a button that you can click to fetch a random dog. When the button is clicked, a `GET` request is made to the [Dog API](https://dog.ceo/dog-api/), and when the dog is received, we show it off in an ` ` tag.
+
+A typical implementation with [React Hooks](https://reactjs.org/docs/hooks-intro.html) might look like this:
+
+```ts
+function DogFetcher() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [dog, setDog] = useState(null);
+
+ return (
+
+
{dog && }
+
+
{
+ setIsLoading(true);
+ fetch(`https://dog.ceo/api/breeds/image/random`)
+ .then((data) => data.json())
+ .then((response) => {
+ setDog(response.message);
+ setIsLoading(false);
+ });
+ }}
+ >
+ {isLoading ? 'Fetching...' : 'Fetch dog!'}
+
+
+ );
+}
+```
+
+This works, but there’s one immediate problem: clicking the button more than once (while a dog is loading) will display one dog briefly, and then replace that dog with another dog. That’s not very considerate to the first dog.
+
+The typical solution to this is to add a `disabled={isLoading}` attribute to the button:
+
+```ts
+function DogFetcher() {
+ // ...
+
+ {
+ // ... excessive amount of ad-hoc logic
+ }}
+ disabled={isLoading}
+ >
+ {isLoading ? 'Fetching...' : 'Fetch dog!'}
+ ;
+
+ // ...
+}
+```
+
+This also works; you’re probably satisfied with this solution. Allow me to burst this bubble.
+
+## What can possibly go wrong?
+
+Currently, the logic reads like this:
+
+> When the button is clicked, fetch a new random dog, and set a flag to make sure that the button cannot be clicked again to fetch a dog while one is being fetched.
+
+However, the logic you _really_ want is this:
+
+> When a new dog is requested, fetch it and make sure that another dog can’t be fetched at the same time.
+
+See the difference? The desired logic is completely separate from the button being clicked; it doesn’t matter _how_ the request is made; it only matters what logic happens afterwards.
+
+Suppose that you want to add the feature that double-clicking the image loads a new dog. What would you have to do?
+
+It’s all too easy to forget to add the same “guard” logic on `figure` (after all, `` won’t work, go figure), but let’s say you’re an astute developer who remembers to add this logic:
+
+```ts
+ function DogFetcher() {
+ // ...
+
+ {
+ if (isLoading) return;
+
+ // copy-paste the fetch logic from the button onClick handler
+ }}
+ >
+ {/* ... */}
+
+
+ // ...
+
+ {
+ // fetch logic
+ }}
+ disabled={isLoading}
+ >
+ {/* ... */}
+
+
+ // ...
+ }
+```
+
+In reality, you can think about this as any use-case where some sort of “trigger” can happen from multiple locations, such as:
+
+- a form being able to be submitted by pressing “Enter” in an input or clicking the “Submit” button
+- an event being triggered by a user action _or_ a timeout
+- any app logic that needs to be shared between different platforms with different event-handling implementations (think React Native)
+
+But there’s a code smell here. Our same fetch logic is implemented in more than one place, and understanding the app logic requires developers to jump around in multiple parts of the code base, finding all of the event handlers where there are tidbits of logic and connecting them together mentally.
+
+## DRYing up the splashes of logic
+
+Okay, so putting logic in our event handlers is probably not a good idea, but we can’t exactly put our finger on the reason why yet. Let’s move the fetch logic out into a function:
+
+```ts
+function DogFetcher() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [dog, setDog] = useState(null);
+
+ function fetchDog() {
+ if (isLoading) return;
+
+ setIsLoading(true);
+ fetch(`https://dog.ceo/api/breeds/image/random`)
+ .then((data) => data.json())
+ .then((response) => {
+ setDog(response.message);
+ setIsLoading(false);
+ });
+ }
+
+ return (
+
+
+ {dog && }
+
+
+
+ {isLoading ? 'Fetching...' : 'Fetch dog!'}
+
+
+ );
+}
+```
+
+## Adding features and complexity
+
+Now let’s see what happens when we want to add basic “features”, such as:
+
+- If fetching a dog fails, an error should be shown.
+- Fetching a dog should be cancellable.
+
+I hesitate to call these “features” because these types of behaviors should be naturally enabled by the programming patterns used, but let’s try to add them anyhow:
+
+```ts
+function DogFetcher() {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [canceled, setCanceled] = useState(false);
+ const [dog, setDog] = useState(null);
+
+ function fetchDog() {
+ setCanceled(false);
+ setError(null);
+ setIsLoading(true);
+
+ fetchRandomDog()
+ .then((response) => {
+ // This should work... but it doesn't!
+ if (canceled) return;
+
+ setIsLoading(false);
+ setDog(response.message);
+ })
+ .catch((error) => {
+ setIsLoading(false);
+ setCanceled(false);
+ setError(error);
+ });
+ }
+
+ function cancel() {
+ setIsLoading(false);
+ setCanceled(true);
+ }
+
+ return (
+
+ {error &&
{error} }
+
+ {dog && }
+
+
+
+ {isLoading ? 'Fetching...' : 'Fetch dog!'}
+
+
Cancel
+
+ );
+}
+```
+
+This _looks_ like it should work -- all of our Boolean flags are being set to the correct values when things happen. However, **it does not work** because of a hard-to-catch bug: _stale callbacks_. In this case, the `canceled` flag inside the `.then(...)` callback will always be the previous value instead of the latest `canceled` value, so cancelling has no effect until the next time we try to fetch a dog, which isn’t what we want.
+
+Hopefully you can see that even with these simple use-cases, our logic has quickly gone out-of-hand, and juggling Boolean flags has made the logic buggier and harder to understand.
+
+## Reducing complexity effectively
+
+Instead of haphazardly adding Boolean flags everywhere, let’s clean this up with the `useReducer` and `useEffect` hooks. These hooks are useful because they express some concepts that lead to better logic organization:
+
+- The `useReducer` hook uses reducers, which return the next state given the current state and some event that just occurred.
+- The `useEffect` hook synchronizes effects with state.
+
+To help us organize the various app states, let’s define a few and put them under a `status` property:
+
+- An `"idle"` status means that nothing happened yet.
+- A `"loading"` status means that the dog is currently being fetched.
+- A `"success"` status means that the dog was successfully fetched.
+- A `"failure"` status means that an error occurred while trying to fetch the dog.
+
+Now let’s define a few events that can happen in the app. Keep in mind: these events can happen from **anywhere**, whether it’s initiated by the user or somewhere else:
+
+- A `"FETCH"` event indicates that fetching a dog should occur.
+- A `"RESOLVE"` event with a `data` property indicates that a dog was successfully fetched.
+- A `"REJECT"` event with an `error` property indicates that a dog was unable to be fetched for some reason.
+- A `"CANCEL"` event indicates that an in-progress fetch should be canceled.
+
+Great! Now let’s write our reducer:
+
+```ts
+function dogReducer(state, event) {
+ switch (event.type) {
+ case 'FETCH':
+ return {
+ ...state,
+ status: 'loading',
+ };
+ case 'RESOLVE':
+ return {
+ ...state,
+ status: 'success',
+ dog: event.data,
+ };
+ case 'REJECT':
+ return {
+ ...state,
+ status: 'failure',
+ error: event.error,
+ };
+ case 'CANCEL':
+ return {
+ ...state,
+ status: 'idle',
+ };
+ default:
+ return state;
+ }
+}
+
+const initialState = {
+ status: 'idle',
+ dog: null,
+ error: null,
+};
+```
+
+Here’s the beautiful thing about this reducer. It is _completely framework-agnostic_ - we can take this and use it in any framework, or no framework at all. And that also makes it much easier to test.
+
+But also, implementing this in a framework becomes _reduced_ (pun intended) to _just dispatching events_. No more logic in event handlers:
+
+```ts
+function DogFetcher() {
+ const [state, dispatch] = useReducer(dogReducer, initialState);
+ const { error, dog, status } = state;
+
+ useEffect(() => {
+ // ... fetchDog?
+ }, [state.status]);
+
+ return (
+
+ {error &&
{error} }
+
dispatch({ type: 'FETCH' })}>
+ {dog && }
+
+
+
dispatch({ type: 'FETCH' })}>
+ {status === 'loading' ? 'Fetching...' : 'Fetch dog!'}
+
+
dispatch({ type: 'CANCEL' })}>Cancel
+
+ );
+}
+```
+
+However, the question remains: how do we execute the side-effect of actually fetching the dog? Well, since the `useEffect` hook is meant for synchronizing effects with state, we can synchronize the `fetchDog()` effect with `status === 'loading'`, since `'loading'` means that that side-effect is being executed anyway:
+
+```ts
+// ...
+useEffect(() => {
+ if (state.status === 'loading') {
+ let canceled = false;
+
+ fetchRandomDog()
+ .then((data) => {
+ if (canceled) return;
+ dispatch({ type: 'RESOLVE', data });
+ })
+ .catch((error) => {
+ if (canceled) return;
+ dispatch({ type: 'REJECT', error });
+ });
+
+ return () => {
+ canceled = true;
+ };
+ }
+}, [state.status]);
+// ...
+```
+
+## The fabled "disabled" attribute
+
+---
+
+The logic above works great. We’re able to:
+
+- Click the “Fetch dog” button to fetch a dog
+- Display a random dog when fetched
+- Show an error if the dog is unable to be fetched
+- Cancel an in-flight fetch request by clicking the “Cancel” button
+- Prevent more than one dog from being fetched at the same time
+
+…all without having to put any logic in the `` attribute. In fact, we completely forgot to do so anyway, and the logic still works!
+
+This is how you know your logic is robust; when it works, regardless of the UI. Whether the “Fetch dog” button is disabled or not, clicking it multiple times in a row won’t exhibit any unexpected behavior.
+
+Also, because most of the logic is delegated to a `dogReducer` function defined _outside_ of your component, it is:
+
+- easy to make into a custom hook
+- easy to test
+- easy to reuse in other components
+- easy to reuse in other _frameworks_
+
+## The final result
+
+Change the ` ` version in the select dropdown to see each of the versions we've explored in this tutorial (even the buggy ones).
+
+## Pushing effects to the side
+
+There’s one lingering thought, though… is `useEffect()` the ideal place to put a side effect, such as fetching?
+
+_Maybe, maybe not._
+
+Honestly, in most use-cases, it works, and it works fine. But it’s difficult to test or separate that effect from your component code. And with the upcoming Suspense and Concurrent Mode features in React, the recommendation is to execute these side-effects when some action triggers them, rather than in `useEffect()`. This is because the official React advice is:
+
+> If you’re working on a data fetching library, there’s a crucial aspect of Render-as-You-Fetch you don’t want to miss. **We kick off fetching before rendering.**
+>
+> [https://reactjs.org/docs/concurrent-mode-suspense.html#start-fetching-early](https://reactjs.org/docs/concurrent-mode-suspense.html#start-fetching-early)
+
+This is good advice. Fetching data should not be coupled with rendering. However, they also say this:
+
+> The answer to this is we want to start fetching in the event handlers instead.
+
+This is misleading advice. Instead, here's what should happen:
+
+1. An event handler should **send a signal** to “something” that indicates that some action just happened (in the form of an event)
+2. That “something” should **orchestrate** what happens next when it receives that event.
+
+Two possible things can happen when an event is received by some orchestrator:
+
+- State can be changed
+- Effects can be executed
+
+All of this can happen outside of the component render cycle, because it doesn't necessarily concern the view. Unfortunately, React doesn't have a built-in way (yet?) to handle state management, side-effects, data fetching, caching etc. outside of the components (we all know Relay is not commonly used), so let's explore one way we can accomplish this completely outside of the component.
+
+## Using a state machine
+
+In this case, we're going to use a state machine to manage and orchestrate state. If you're new to state machines, just know that they feel like your typical Redux reducers with a few more "rules". Those rules have some powerful advantages, and are also the mathematical basis for how literally every computer in existence today works. So they might be worth learning.
+
+I'm going to use [XState](https://xstate.js.org/docs) and [`@xstate/react`](https://xstate.js.org/docs/packages/xstate-react/) to create the machine:
+
+```ts
+import { Machine, assign } from 'xstate';
+import { useMachine } from '@xstate/react';
+
+// ...
+
+const dogFetcherMachine = Machine({
+ id: 'dog fetcher',
+ initial: 'idle',
+ context: {
+ dog: null,
+ error: null,
+ },
+ states: {
+ idle: {
+ on: { FETCH: 'loading' },
+ },
+ loading: {
+ invoke: {
+ src: () => fetchRandomDog(),
+ onDone: {
+ target: 'success',
+ actions: assign({ dog: (_, event) => event.data.message }),
+ },
+ onError: {
+ target: 'failure',
+ actions: assign({ error: (_, event) => event.data }),
+ },
+ },
+ on: { CANCEL: 'idle' },
+ },
+ success: {
+ on: { FETCH: 'loading' },
+ },
+ failure: {
+ on: { FETCH: 'loading' },
+ },
+ },
+});
+```
+
+Notice how the machine looks like our previous reducer, with a couple of differences:
+
+- It looks like some sort of configuration object instead of a switch statement
+- We’re matching on the _state_ first, instead of the _event_ first
+- We’re invoking the `fetchRandomDog()` promise inside the machine! 😱
+
+Don’t worry; we’re not actually executing any side-effects inside of this machine. In fact, `dogFetcherMachine.transition(state, event)` is a _pure function_ that tells you the next state given the current state and event. Seems familiar, huh?
+
+Furthermore, I can copy-paste this exact machine and [visualize it in XState Viz](https://xstate.js.org/viz):
+
+
+
+[View this viz on xstate.js.org/viz](https://xstate.js.org/viz/?gist=414c0e4c40dab1dc80c9218f85605a24)
+
+So what does our component code look like now? Take a look:
+
+```ts
+function DogFetcher() {
+ const [current, send] = useMachine(dogFetcherMachine);
+ const { error, dog } = current.context;
+
+ return (
+
+ {error &&
{error} }
+
send('FETCH')}>
+ {dog && }
+
+
+
send('FETCH')}>
+ {current.matches('loading') && 'Fetching...'}
+ {current.matches('success') && 'Fetch another dog!'}
+ {current.matches('idle') && 'Fetch dog'}
+ {current.matches('failure') && 'Try again'}
+
+
send('CANCEL')}>Cancel
+
+ );
+}
+```
+
+Here’s the difference between using a state machine and a reducer:
+
+- The hook signature for `useMachine(...)` looks almost the same as `useReducer(...)`
+- No fetching logic exists inside the component; it’s all external!
+- There’s a nice `current.matches(...)` function that lets us customize our button text
+- `send(...)` instead of `dispatch(...)`... and it takes a plain string! (Or an object, up to you).
+
+A state machine/statechart defines its transitions from the state because it answers the question: “Which events should be handled _from this state?_” The reason that having `` is fragile is because we admit that some “FETCH” event can cause an effect no matter which state we’re in, so we have to clean up our ~mess~ faulty logic by preventing the user from clicking the button while loading.
+
+Instead, it’s better to be proactive about your logic. Fetching should only happen when the app is not in some `"loading"` state, which is what is clearly defined in the state machine -- the `"FETCH"` event is not handled in the `"loading"` state, which means it has no effect. Perfect.
+
+## Final points
+
+Disabling a button is not logic. Rather, it is a sign that logic is fragile and bug-prone. In my opinion, disabling a button should only be a visual cue to the user that clicking the button _will have no effect_.
+
+So when you’re creating fetching logic (or any other kind of complex logic) in your applications, no matter the framework, ask yourself these questions:
+
+- What are the concrete, finite states this app/component can be in? E.g., "loading", "success", "idle", "failure", etc.
+- What are all the possible events that can occur, regardless of state? This includes events that don't come from the user (such as `"RESOLVE"` or `"REJECT"` events from promises)
+- Which of the finite states should handle these events?
+- How can I organize my app logic so that these events are handled properly in those states?
+
+You do not need a state machine library (like XState) to do this. In fact, you might not even need `useReducer` when you’re first adopting these principles. Even something as simple as having a state variable representing a finite state can already clean up your logic plenty:
+
+```ts
+function DogFetcher() {
+ // 'idle' or 'loading' or 'success' or 'error'
+ const [status, setStatus] = useState('idle');
+}
+```
+
+And just like that, you’ve eliminated `isLoading`, `isError`, `isSuccess`, `startedLoading`, and whatever Boolean flags you were going to create. And if you really start to miss that `isLoading` flag (for whatever reason), you can still have it, but ONLY if it’s derived from your organized, finite states. The `isLoading` variable should NEVER be a primary source of state:
+
+```ts
+function DogFetcher() {
+ // 'idle' or 'loading' or 'success' or 'error'
+ const [status, setStatus] = useState('idle');
+
+ const isLoading = status === 'loading';
+
+ return (
+ // ...
+ {/* ... */}
+ // ...
+ );
+}
+```
+
+And we’ve come full circle. Thanks for reading.
diff --git a/fumadocs/content/blog/2019-12-09-xstate-version-47-and-the-future/index.mdx b/fumadocs/content/blog/2019-12-09-xstate-version-47-and-the-future/index.mdx
new file mode 100644
index 000000000..edfec2755
--- /dev/null
+++ b/fumadocs/content/blog/2019-12-09-xstate-version-47-and-the-future/index.mdx
@@ -0,0 +1,258 @@
+---
+title: 'XState: version 4.7 and the future'
+description: >-
+ XState version 4.7 has just been released. This is a minor version bump, but a
+ major reworking of the internal algorithms, a lot of new capabilites, bug
+ fixes and a better TypeScript experience.
+tags: [update, state machine, statechart, xstate, state]
+authors: [david]
+image: /blog/2019-12-09-xstate-version-47-and-the-future.png
+slug: 2019-12-09-xstate-version-47-and-the-future
+date: 2019-12-09
+---
+
+
+
+
+
+[XState](https://github.com/davidkpiano/xstate) version 4.7 [has just been released](https://github.com/davidkpiano/xstate/releases/tag/v4.7.0). This is a minor version bump, but a major reworking of the internal algorithms, a lot of new capabilities, bug fixes, and a better TypeScript experience. It also paves the road for even more utilities, like `@xstate/test` and `@xstate/react`, as well as compatibility with other 3rd-party tools across the ecosystem, and even across languages.
+
+{/* truncate */}
+
+## What is XState?
+
+XState is a JavaScript (and TypeScript) library for creating state machines and statecharts, and interpreting them. State machines enforce a specific set of ”rules“ on logic structure such that:
+
+- There are a finite number of **states** (such as `"loading"` or `"success"`), which is different than _context_ (related data with potentially infinite possibilities, such as `email` or `age`)
+- There are a finite number of **events** (such as `{ type: 'FETCH', query: "..." }` that can trigger a transition between states.
+- Each state has **transitions**, which say, ”Given some **event**, go to this next state and/or do these actions”.
+
+You don’t need a state machine library to do this, as you can use `switch` statements instead:
+
+```ts
+ switch (state.status) {
+ case 'idle': // finite state
+ switch (event.type) {
+ case 'FETCH':
+ return {
+ ...state,
+ status: 'loading',
+ query: event.query
+ };
+ // ...
+ // ...
+ // ...
+ }
+```
+
+But let’s be honest, writing it like this is arguably a bit cleaner:
+
+```ts
+const machine = Machine({
+ initial: 'idle',
+ states: {
+ idle: {
+ on: {
+ FETCH: {
+ target: 'loading',
+ actions: assign({ query: (_, event) => event.query }),
+ },
+ },
+ },
+ // ...
+ },
+});
+```
+
+And it also makes it possible to directly copy-paste this machine code into a visualizer, like [XState Viz](https://xstate.js.org/viz), and visualize it, like was done at the end of the [No, disabling a button is not app logic](https://dev.to/davidkpiano/no-disabling-a-button-is-not-app-logic-598i) article:
+
+
+[View this viz on XState Viz](https://xstate.js.org/viz/?gist=414c0e4c40dab1dc80c9218f85605a24)
+
+Then there are **statecharts**, which are an extension of finite state machines created by [David Harel in 1989 (read the paper 📑)](http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf). Statecharts offer many improvements and mitigate many issues of using flat finite state machines, such as:
+
+- Nested states (hierarchy)
+- Parallel states (orthogonality)
+- History states
+- Entry, exit, and transition actions
+- Transient states
+- Activities (ongoing actions)
+- Communication with many machines (invoked services)
+- Delayed transitions
+- And much more
+
+These are things that you _definitely_ do not want to implement yourself, which is why libraries like XState exist. And this brings us to…
+
+## What is new in XState 4.7?
+
+This minor release has been worked on for months, with a huge amount of help from [Mateusz Burzyński (also known as AndaristRake)](https://twitter.com/AndaristRake) 👏. The reason it took so long was because we are internally reworking the algorithms to be simpler, fit the [SCXML spec](https://www.w3.org/TR/scxml/#InternalStructureofEvents), and be compatible with a growing number of tools in the ecosystem. This refactoring also makes adding new capabilities much easier, and will hopefully encourage more contributors to help with this project. As a nice side-effect, it also eliminates a few edge-case bugs that had workarounds, but might have caused a suboptimal developer experience in previous versions.
+
+## Refactored internal algorithms
+
+How difficult can it be to create a statechart library? A lot more difficult than it seems, especially if you want to conform to the long, but well-established [SCXML spec](https://www.w3.org/TR/scxml). There’s even libraries for integrating SCXML code directly with JavaScript, such as Jacob Beard’s excellent [SCION tools](http://scion.scxml.io/), which I highly recommend you check out. It was a huge inspiration for XState, and XState is tested against much of the same code.
+
+SCXML specifies an [algorithm for SCXML interpretation](https://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation), which is written in pseudocode, but directly transferable to many popular languages. This algorithm was followed more closely in the refactor, which simplified a lot of the code base and removed the need for ad-hoc data structures such as `StateTree`, which was used to keep track of which state nodes were "active" for a given transition (now it’s just a set).
+
+As a result, the core code base is a little smaller, the algorithms are a little bit faster (determining the next state is basically an O(1) lookup, O(n) worst-case), and the code base is a lot nicer to work with and contribute to. We will continue to improve the algorithms used as we move towards 5.0.
+
+## Typestates
+
+[Typestates](https://en.wikipedia.org/wiki/Typestate_analysis) are really useful for developers. They’re popular in Rust, and this article on [The Typestate Pattern in Rust](http://cliffle.com/blog/rust-typestate/) describes them elegantly:
+
+> Typestates are a technique for moving **properties of state** (the dynamic information a program is processing) **into the type level** (the static world that the compiler can check ahead-of-time).
+
+Without learning Rust or diving into the Wikipedia article, let’s present a classic example: loading data. You might represent the state’s context in this way:
+
+```ts
+interface User {
+ name: string;
+}
+
+interface UserContext {
+ user?: User;
+ error?: string;
+}
+```
+
+This type-safe declaration allows you to defensively program effectively, but it can be a bit annoying when you are 100% sure that `user` is defined:
+
+```ts
+if (state.matches('success')) {
+ if (!state.context.user) {
+ // this should be impossible!
+ // the user definitely exists!
+ throw new Error('Something weird happened');
+ }
+
+ return state.context.user.name;
+}
+```
+
+In 4.7, XState allows you to [represent your states with Typestates](https://xstate.js.org/docs/guides/typescript.html#typestates) so that you can tell the compiler that you know how the `context` should be in any given state:
+
+
+
+This is very useful and improves the developer experience, but should be used as a strong guide, and not as a guarantee. It works by using [discriminated unions in TypeScript](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html) to define your states, but the way it is implemented requires TypeScript version 3.7 and higher. There’s still some quirks to work out, as we’re basically trying to trick TypeScript into knowing some extra information about our state machines that is otherwise difficult/impossible to infer in a statically typed language. (Maybe one day JavaScript will have a dependently-typed flavor.)
+
+## Better service experience
+
+XState makes invoking external ”services” a first-class citizen. If this is a foreign concept, for now, just understand that it answers the question “how can many state machines communicate with each other?”, and the answer is by using events as the main communication mechanism. In 4.7, the developer experience for this is improved:
+
+- Invoked services can now be referenced on the `state.children` object by their ID. So if a state invokes some service with `id: 'fetchUser'`, then that invocation will be present on `state.children.fetchUser`.
+- The new `forwardTo()` action creator allows you to forward events to invoked services, which cuts down a lot of boilerplate:
+
+```ts
+on: {
+ SOME_EVENT: {
+ actions: forwardTo('someService');
+ }
+}
+```
+
+- SCXML has this notion of a `sessionid`, which is a unique identifier for each invoked service. XState 4.7 becomes more SCXML-compatible by keeping a reference of this in `state._sessionid`, which corresponds to the SCXML `_sessionid` variable.
+- XState can use that `_sessionid` to determine which service sent an event, so it can respond with an event back, using the new `respond()` action creator:
+
+```ts
+const authServerMachine = Machine({
+ initial: 'waitingForCode',
+ states: {
+ waitingForCode: {
+ on: {
+ CODE: {
+ actions: respond('TOKEN', { delay: 10 }),
+ },
+ },
+ },
+ },
+});
+
+const authClientMachine = Machine({
+ initial: 'idle',
+ states: {
+ idle: {
+ on: { AUTH: 'authorizing' },
+ },
+ authorizing: {
+ invoke: {
+ id: 'auth-server',
+ src: authServerMachine,
+ },
+ entry: send('CODE', { to: 'auth-server' }),
+ on: {
+ TOKEN: 'authorized',
+ },
+ },
+ authorized: {
+ type: 'final',
+ },
+ },
+});
+```
+
+You can make your own custom action creators too, and implement patterns that you might be familiar with already if you’ve worked with microservices.
+
+## Wildcard descriptors
+
+If you’ve ever wanted to transition from a state if _any_ (unspecified) event is received? Well, you’re in luck, because XState now supports [wildcard descriptors](https://xstate.js.org/docs/guides/transitions.html#wildcard-descriptors), which are a type of [event descriptor (SCXML)](https://www.w3.org/TR/scxml/#EventDescriptors) that describes a transition for any event in a given state:
+
+```ts
+const quietMachine = Machine({
+ id: 'quiet',
+ initial: 'idle',
+ states: {
+ idle: {
+ on: {
+ WHISPER: undefined,
+ // On any event besides a WHISPER, transition to the 'disturbed' state
+ '*': 'disturbed',
+ },
+ },
+ disturbed: {},
+ },
+});
+
+quietMachine.transition(quietMachine.initialState, 'WHISPER');
+// => State { value: 'idle' }
+
+quietMachine.transition(quietMachine.initialState, 'SOME_EVENT');
+// => State { value: 'disturbed' }
+```
+
+## Much, much more
+
+See [https://github.com/davidkpiano/xstate/releases/tag/v4.7.0](https://github.com/davidkpiano/xstate/releases/tag/v4.7.0) for an overview of the latest updates in this minor version.
+
+## The future of XState
+
+All this leads to the big question: what are the future plans/goals for XState? The first important thing to realize is that XState is _not just another state management library_, and state management was never its only goal. XState strives to bring two things to the JavaScript ecosystem:
+
+- **State machines/statecharts**, for modeling the logic of any individual component
+- **Actor model**, for modeling how components communicate with each other and behave in a system
+
+All of these are very old, very useful, and battle-tested concepts. The benefits they provide cannot be understated, and highlight the future plans for XState and related tooling:
+
+- Better visualization tools, including an updated visualizer, dev tools for Firefox and Chrome (work in progress!), dev tools for VS Code, and integration with other graphical viz tools such as [PlantUML](https://plantuml.com/) and [GraphViz](https://www.graphviz.org/)
+- Full [SCXML](https://www.w3.org/TR/scxml/) compatibility, which will allow statecharts authored in XState to be reusable in other languages that have SCXML tooling, as it is a truly language-agnostic spec
+- A catalog of examples, to demonstrate common patterns and best practices for many use-cases
+- Analytics, testing, and simulation tools
+
+As well as some initial ideas for XState version 5.0:
+
+- Better type safety and a more seamless TypeScript experience
+- Static analysis tools for compile-time hints/warnings and run-time optimizations
+- A more ”functional”, and completely optional, syntax for defining states and transitions more naturally (developer experience)
+- Higher-level state types such as `"task"` and `"choice"` to make it easier to define workflows and remove some boilerplate
+
+We’re also listening to ideas that you present to us in the [XState Wish List](https://spectrum.chat/statecharts/general/xstate-wish-list~6f025b10-fcbc-4ab5-ae59-5201112f06f2) thread, so post what you would like to see!
+
+## More information
+
+If you’re curious about XState or statecharts in general, there are many fantastic resources, including:
+
+- [The World of Statecharts](https://statecharts.github.io/) by Erik Mogensen
+- [Statecharts community](https://spectrum.chat/statecharts) on Spectrum
+- [XState docs](https://xstate.js.org/docs)
+- [Other tutorials](https://xstate.js.org/docs/about/tutorials.html) made by many excellent developers in the community
diff --git a/fumadocs/content/blog/2020-01-20-redux-is-half-a-pattern-1-2/index.mdx b/fumadocs/content/blog/2020-01-20-redux-is-half-a-pattern-1-2/index.mdx
new file mode 100644
index 000000000..56a7f1489
--- /dev/null
+++ b/fumadocs/content/blog/2020-01-20-redux-is-half-a-pattern-1-2/index.mdx
@@ -0,0 +1,487 @@
+---
+title: Redux is half of a pattern (1/2)
+description: Learn how Redux, and all other state management libraries, have one thing in common - they are all partial implementations of state machines - and how we can improve the way we model app state and logic.
+tags: [redux, state machine, statechart, state]
+date: 2020-01-20
+authors: [david]
+image: /blog/2020-01-20-redux-is-half-a-pattern-1-2.png
+slug: 2020-01-20-redux-is-half-a-pattern-1-2
+---
+
+
+
+
+
+Redux is fantastic.
+
+Some of you might disagree, so let me tell you why.
+
+Over the last few years, Redux has popularized the idea of using message-passing (also known as [event-driven programming](https://en.wikipedia.org/wiki/Event-driven_programming)) to manage application state. Instead of making arbitrary method calls to various class instances or mutating data structures, we now can think of state as being in a "predictable container" that only changes as a reaction to these "events".
+
+{/* truncate */}
+
+This simple idea and implementation is universal enough to be used with any framework (or no framework at all), and has inspired libraries for other popular frameworks such as:
+
+- [Vuex](https://vuex.vuejs.org/) for Vue
+- [NgRx](https://ngrx.io/) for Angular
+
+However, Redux has recently come under scrutiny by some prominent developers in the web community:
+
+
+
+
+
+If you don't know these developers, they are the co-creators of Redux themselves. So why have Dan and Andrew, and many other developers, all but forsaken the use of Redux in applications?
+
+The ideas and patterns in Redux appear sound and reasonable, and Redux is still used in many large-scale production apps today. However, it forces a certain architecture in your application:
+
+
+
+As it turns out, this kind of single-atom immutable architecture is _not natural_ nor does it represent how any software application works (nor should work) in the real-world.
+
+Redux is an alternative implementation of Facebook's [Flux "pattern"](https://facebook.github.io/flux). Many sticking points and hardships with Facebook's implementation have led developers to seek out alternative, nicer, more developer-friendly APIs such as Redux, Alt, Reflux, Flummox, [and many more.](https://github.com/kriasoft/react-starter-kit/issues/22). Redux emerged as a [clear winner](https://facebook.github.io/flux/docs/related-libraries/#redux---alternative-state-management), and it is stated that Redux combines the ideas from:
+
+- [The Command pattern](https://www.wikiwand.com/en/Command_pattern)
+- [The Elm Architecture](https://guide.elm-lang.org/architecture/)
+
+However, not even the Elm architecture is a standalone architecture/pattern, as it is based on fundamental patterns, whether developers know it or not:
+
+> Rather than someone inventing it, early Elm programmers kept discovering the same basic patterns in their code. It was kind of spooky to see people ending up with well-architected code without planning ahead!
+
+
+
+In this post, I will highlight some of the reasons that Redux is _not_ a standalone pattern by comparing it to a fundamental, well-established pattern: the **finite state machine**. This is not an arbitrary choice; every single application that we write is basically a state machine, whether we know it or not. The difference is that the state machines we write are implicitly defined.
+
+I hope that some of these comparisons and differences will help you realize how some of the common pain points in Redux-driven applications materialize, and how you can use this existing pattern to help you craft a better state management architecture, whether you're using Redux, another library, or no library at all.
+
+## What is a finite state machine?
+
+(Taken from another article I wrote, [The FaceTime Bug and the Dangers of Implicit State Machines](https://medium.com/@DavidKPiano/the-facetime-bug-and-the-dangers-of-implicit-state-machines-a5f0f61bdaa2)):
+
+Wikipedia has a [useful but technical description](https://en.wikipedia.org/wiki/Finite-state_machine) on what a finite state machine is. In essence, a finite state machine is a computational model centered around states, events, and transitions between states. To make it simpler, think of it this way:
+
+- Any software you make can be described in a **finite number of states** (e.g., `idle`, `loading`, `success`, `error`)
+- You can only be in **one** of those states at any given time (e.g., you can’t be in the `success` and `error` states at the same time)
+- You always start at some **initial state** (e.g., `idle`)
+- You move from state to state, or **transition**, based on events (e.g., from the `idle` state, when the `LOAD` event occurs, you immediately transition to the `loading` state)
+
+It’s like the software that you’re used to writing, but with more explicit rules. You might have been used to writing `isLoading` or `isSuccess` as Boolean flags before, but state machines make it so that you’re not allowed to have `isLoading === true && isSuccess === true` at the same time.
+
+It also makes it _visually clear_ that event handlers can only do one main thing: forward their events to a state machine. They’re not allowed to “escape” the state machine and execute business logic, just like real-world physical devices: buttons on calculators or ATMs don’t actually do operations or execute actions; rather, they send "signals" to some central unit that manages (or _orchestrates_) state, and that unit decides what should happen when it receives that "signal".
+
+## What about state that is not finite?
+
+With state machines, especially [UML state machines (a.k.a. statecharts)](https://en.wikipedia.org/wiki/UML_state_machine), "state" refers to something different than the data that doesn't fit neatly into finite states, but both "state" and what's known as ["extended state"](https://en.wikipedia.org/wiki/UML_state_machine#Extended_states) work together.
+
+For example, let's consider water 🚰. It can fit into one of four phases, and we consider these the _states_ of water:
+
+- `liquid`
+- `solid` (e.g., ice, frost)
+- `gas` (e.g., vapor, steam)
+- `plasma`
+
+{/* [](https://www.uml-diagrams.org/examples/water-phase-uml-state-machine-diagram-example.html) */}
+
+> Water phase UML state machine diagram from [uml-diagrams.com](https://www.uml-diagrams.org/examples/water-phase-uml-state-machine-diagram-example.html)
+
+However, the temperature of water is a _continuous_ measurement, not a discrete one, and it can't be represented in a finite way. Despite this, water temperature can be represented alongside the finite state of water, e.g.:
+
+- `liquid` where `temperature === 90` (celsius)
+- `solid` where `temperature === -5`
+- `gas` where `temperature === 500`
+
+There's many ways to represent the combination of finite and extended state in your application. For the water example, I would personally call the finite state `value` (as in the "finite state value") and the extended state `context` (as in "_contextual_ data"):
+
+```js
+const waterState = {
+ value: 'liquid', // finite state
+ context: {
+ // extended state
+ temperature: 90,
+ },
+};
+```
+
+But you're free to represent it in other ways:
+
+```js
+const waterState = {
+ phase: 'liquid', // finite state
+ data: {
+ // extended state
+ temperature: 90,
+ },
+};
+
+// or...
+
+const waterState = {
+ status: 'liquid', // finite state
+ temperature: 90, // anything not 'status' is extended state
+};
+```
+
+The key point is that there is a clear distinction between **finite** and **extended** state, and there is logic that prevents the application from reaching an _impossible state_, e.g.:
+
+```js
+const waterState = {
+ isLiquid: true,
+ isGas: true, // 🚱 Water can't be both liquid and gas simultaneously!
+ temperature: -50, // ❄️ This is ice!! What's going on??
+};
+```
+
+And we can extend these examples to realistic code, such as changing this:
+
+```js
+const userState = {
+ isLoading: true,
+ isSuccess: false,
+ user: null,
+ error: null,
+};
+```
+
+To something like this:
+
+```js
+const userState = {
+ status: 'loading', // or 'idle' or 'error' or 'success'
+ user: null,
+ error: null,
+};
+```
+
+This prevents impossible states like `userState.isLoading === true` and `userState.isSuccess === true` happening simultaneously.
+
+## How does Redux compare to a finite state machine?
+
+The reason I'm comparing Redux to a state machine is because, from a birds-eye view, their state management models look pretty similar. For Redux:
+
+> `state` + `action` = `newState`
+
+For state machines:
+
+> `state` + `event` = `newState` + `effects`
+
+In code, these can even be represented the same way, by using a [reducer](https://redux.js.org/basics/reducers/):
+
+```js
+function userReducer(state, event) {
+ // Return the next state, which is
+ // determined based on the current `state`
+ // and the received `event` object
+
+ // This nextState may contain a "finite"
+ // state value, as well as "extended"
+ // state values.
+
+ // It may also contain side-effects
+ // to be executed by some interpreter.
+ return nextState;
+}
+```
+
+There are already some subtle differences, such as "action" vs. "event" or how extended state machines model side-effects ([they do](https://en.wikipedia.org/wiki/UML_state_machine#Actions_and_transitions)). Dan Abramov even recognizes some of the differences:
+
+
+
+A reducer can be used to implement a finite state machine, but most reducers are _not_ modeled as finite state machines. Let's change that by learning some of the differences between Redux and state machines.
+
+## Difference: finite & extended states
+
+Typically, a Redux reducer's state will not make a clear distinction between "finite" and "extended" states, as previously mentioned above. This is an important concept in state machines: an application is always in _exactly one_ of a finite number of "states", and the rest of its data is represented as its extended state.
+
+Finite states can be introduced to a reducer by making an explicit property that represents exactly one of the many possible states:
+
+```js
+const initialUserState = {
+ status: 'idle', // explicit finite state
+ user: null,
+ error: null,
+};
+```
+
+What's great about this is that, if you're using TypeScript, you can take advantage of using [discriminated unions](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html) to make impossible states impossible:
+
+```js
+interface User {
+ name: string;
+ avatar: string;
+}
+
+type UserState =
+ | { status: 'idle', user: null, error: null }
+ | { status: 'loading', user: null, error: null }
+ | { status: 'success', user: User, error: null }
+ | { status: 'failure', user: null, error: string };
+```
+
+## Difference: events vs. actions
+
+In [state machine terminology](https://en.wikipedia.org/wiki/UML_state_machine#Actions_and_transitions), an "action" is a side-effect that occurs as the result of a transition:
+
+> When an event instance is dispatched, the state machine responds by **performing actions**, such as changing a variable, performing I/O, invoking a function, generating another event instance, or changing to another state.
+
+This isn't the only reason that using the term "action" to describe something that causes a state transition is confusing; "action" also suggests something that needs to be done (i.e., a command), rather than something that just happened (i.e., an event).
+
+So keep the following terminology in mind when we talk about state machines:
+
+- An **event** describes something that occurred. Events trigger state transitions.
+- An **action** describes a side-effect that should occur as a _response_ to a state transition.
+
+The [Redux style guide](https://redux.js.org/style-guide/style-guide/) also directly suggests modeling actions as events:
+
+> However, we recommend trying to treat actions more as "describing events that occurred", rather than "setters". Treating actions as "events" generally leads to more meaningful action names, fewer total actions being dispatched, and a more meaningful action log history.
+>
+> _Source: [Redux style guide: Model actions as events, not setters](https://redux.js.org/style-guide/style-guide/#model-actions-as-events-not-setters)_
+
+When the word "event" is used in this article, that has the same meaning as a conventional Redux action object. For side-effects, the word "effect" will be used.
+
+
+
+## Difference: explicit transitions
+
+Another fundamental part of how state machines work are **transitions**. A transition describes how one finite state transitions to another finite state due to an event. This can be represented using boxes and arrows:
+
+
+
+This diagram makes it clear that it's impossible to transition directly from, e.g., `idle` to `success` or from `success` to `error`. There are clear sequences of events that need to occur to transition from one state to another.
+
+However, the way that developers tend to model reducers is by determining the next state solely on the received event:
+
+```js
+function userReducer(state, event) {
+ switch (event.type) {
+ case 'FETCH':
+ // go to some 'loading' state
+ case 'RESOLVE':
+ // go to some 'success' state
+ case 'REJECT':
+ // go to some 'error' state
+ default:
+ return state;
+ }
+}
+```
+
+The problem with managing state this way is that it does not prevent _impossible transitions_. Have you ever seen a screen that briefly displays an error, and then shows some success view? If you haven't, browse [Reddit](https://reddit.com), and do the following steps:
+
+1. Search for anything.
+2. Click on the "Posts" tab while the search is happening.
+3. Say "aha!" and wait a couple seconds.
+
+In step 3, you'll probably see something like this (visible at the time of publishing this article):
+
+
+
+After a couple seconds, this unexpected view will disappear and you will finally see search results. This bug has been present for a while, and even though it's innocuous, it's not the best user experience, and it can definitely be considered faulty logic.
+
+However it is implemented (Reddit _does_ use Redux...), something is definitely wrong: _an impossible state transition happened_. It makes absolutely no sense to transition directly from the "error" view to the "success" view, and in this case, the user shouldn't see an "error" view anyway because it's not an error; it's still loading!
+
+You might be looking through your existing Redux reducers and realize where this potential bug may surface, because by basing state transitions only on events, these impossible transitions become possible to occur. Sprinkling if-statements all over your reducer might alleviate the symptoms of this:
+
+```js
+function userReducer(state, event) {
+ switch (event.type) {
+ case 'FETCH':
+ if (state.status !== 'loading') {
+ // go to some 'loading' state...
+ // but ONLY if we're not already loading
+ }
+
+ // ...
+ }
+}
+```
+
+But that only makes your state logic harder to follow because the state transitions are _not explicit_. Even though it might be a little more verbose, it's better to determine the next state based on both the current finite state and the event, rather than just on the event:
+
+```js
+function userReducer(state, event) {
+ switch (state.status) {
+ case 'idle':
+ switch (event.type) {
+ case 'FETCH':
+ // go to some 'loading' state
+
+ // ...
+ }
+
+ // ...
+ }
+}
+```
+
+You can even split this up into individual "finite state" reducers, to make things cleaner:
+
+```js
+function idleUserReducer(state, event) {
+ switch (event.type) {
+ case 'FETCH':
+ // go to some 'loading' state
+
+ // ...
+ }
+ default:
+ return state;
+ }
+}
+
+function userReducer(state, event) {
+ switch (state.status) {
+ case 'idle':
+ return idleUserReducer(state, event);
+ // ...
+ }
+}
+```
+
+But don't just take my word for it. The Redux style guide also strongly recommends treating your reducers as state machines:
+
+> [...] treat reducers as "state machines", where the combination of both the current state and the dispatched action determines whether a new state value is actually calculated, not just the action itself unconditionally.
+>
+> _Source: [Redux style guide: treat reducers as state machines](https://redux.js.org/style-guide/style-guide/#treat-reducers-as-state-machines)_
+
+I also talk about this idea in length in my post: [No, disabling a button is not app logic.](https://dev.to/davidkpiano/no-disabling-a-button-is-not-app-logic-598i)
+
+## Difference: declarative effects
+
+If you look at Redux in isolation, its strategy for managing and executing side-effects is this:
+
+> ¯\\\_(ツ)\_/¯
+
+That's right; Redux has no built-in way of handling side-effects. In any non-trivial application, you _will_ have side-effects if you want to do anything useful, such as make a network request or kick off some sort of async process. Importantly enough, side-effects should _not_ be considered an afterthought; they should be treated as a first-class citizen and uncompromisingly represented in your application logic.
+
+Unfortunately, with Redux, they are, and the only solution is to use [middleware](https://redux.js.org/advanced/middleware), which is inexplicably an advanced topic, despite being required for any non-trivial app logic:
+
+> Without middleware, Redux store only supports synchronous data flow.
+>
+> _Source: [Redux docs: Async Flow](https://redux.js.org/advanced/async-flow)_
+
+With extended/UML state machines (also known as statecharts), these side-effects are known as **actions** (and will be referred to as actions for the rest of this post) and are declaratively modeled. Actions are the direct result of a transition:
+
+> When an event instance is dispatched, the state machine responds by **performing actions**, such as changing a variable, performing I/O, invoking a function, generating another event instance, or changing to another state.
+>
+> \_Source: [(Wikipedia) UML State Machine: Actions and Transitions](https://en.wikipedia.org/wiki/UML_state_machine#Actions_and_transitions)
+
+This means that when an event changes state, actions (effects) may be executed as a result, even if the state stays the same (known as a "self-transition"). Just like Newton said:
+
+> For every action, there is an equal and opposite reaction.
+>
+> _Source: Newton's Third Law of Motion_
+
+Actions _never_ occur spontaneously, without cause; not in software, not in hardware, not in real life, never. There is _always_ a cause for an action to occur, and with state machines, that cause is a state transition, due to a received event.
+
+Statecharts distinguish how actions are determined in three possible ways:
+
+- **Entry actions** are effects that are executed whenever a specific finite state is entered
+- **Exit actions** are effects that are executed whenever a specific finite state is exited
+- **Transition actions** are effects that are executed whenever a specific transition between two finite states is taken.
+
+Fun fact: this is why statecharts are said to have the characteristic of both [Mealy machines](https://en.wikipedia.org/wiki/Mealy_machine) and [Moore machines](https://en.wikipedia.org/wiki/Moore_machine):
+
+- With Mealy machines, "output" (actions) depends on the state and the event (transition actions)
+- With Moore machines, "output" (actions) depends on just the state (entry & exit actions)
+
+The original philosophy of Redux is that it did not want to be opinionated on how these side-effects are executed, which is why middleware such as [redux-thunk](https://github.com/reduxjs/redux-thunk) and [redux-promise](https://github.com/redux-utilities/redux-promise) exist. These libraries work around the fact that Redux is side-effect-agnostic by having third-party, use-case specific "solutions" for handling different types of effects.
+
+So how can this be solved? It may seem weird, but just like you can use a property to specify finite state, you can also use a property to specify _actions that should be executed_ in a declarative way:
+
+```js
+// ...
+case 'FETCH':
+ return {
+ ...state,
+
+ // finite state
+ status: 'loading',
+
+ // actions (effects) to execute
+ actions: [
+ { type: 'fetchUser', id: 42 }
+ ]
+ }
+// ...
+```
+
+Now, your reducer will return useful information that answers the question, "what side-effects (actions) should be executed as a result of this state transition?" The answer is clear and colocated right in your app state: read the `actions` property for a declarative description of the actions to be executed, and execute them:
+
+```js
+// pretend the state came from a Redux React hook
+const { actions } = state;
+
+useEffect(() => {
+ actions.forEach((action) => {
+ if (action.type === 'fetchUser') {
+ fetch(`/api/user/${action.id}`)
+ .then((res) => res.json())
+ .then((data) => {
+ dispatch({ type: 'RESOLVE', user: data });
+ });
+ }
+ // ... etc. for other action implementations
+ });
+}, [actions]);
+```
+
+Having side-effects modeled declaratively in some `state.actions` property (or similar) has some great benefits, such as in predicting/testing or being able to trace when actions will or have been executed, as well as being able to customize the implementation details of executing those actions. For instance, the `fetchUser` action can be changed to read from a cache instead, all without changing any of the logic in the reducer.
+
+## Difference: sync vs. async data flow
+
+The fact is that middleware is indirection. It fragments your application logic by having it present in multiple places (the reducers and the middleware) without a clear, cohesive understanding of how they work together. Furthermore, it makes some use-cases easier but others much more difficult. For example: take this example from the [Redux advanced tutorial](https://redux.js.org/advanced/example-reddit-api), which uses `redux-thunk` to allow dispatching a "thunk" for making an async request:
+
+```js
+function fetchPosts(subreddit) {
+ return (dispatch) => {
+ dispatch(requestPosts(subreddit));
+ return fetch(`https://www.reddit.com/r/${subreddit}.json`)
+ .then((response) => response.json())
+ .then((json) => dispatch(receivePosts(subreddit, json)));
+ };
+}
+```
+
+Now ask yourself: _how can I cancel this request?_ With `redux-thunk`, it simply isn't possible. And if your answer is to "choose a different middleware", you just validated the previous point. Modeling logic should not be a question of which middleware you choose, and middleware shouldn't even be part of the state modeling process.
+
+As previously mentioned, the only way to model async data flow with Redux is by using middleware. And with all the possible use-cases, from thunks to Promises to sagas (generators) to epics (observables) and more, the ecosystem has plenty of different solutions for these. But the ideal number of solutions is _one_: the solution provided by the pattern in use.
+
+Alright, so how do state machines solve the async data flow problem?
+
+_They don't._
+
+To clarify, state machines do not distinguish between sync and async data flows, because there is no difference. This is such an important realization to make, because not only does it simplify the idea of data flow, but it also models how things work in real life:
+
+- A state transition (triggered by a received event) always occurs in "zero-time"; that is, states synchronously transition.
+- Events can be received at any time.
+
+There is no such thing as an asynchronous transition. For example, modeling data fetching doesn't look like this:
+
+```
+idle . . . . . . . . . . . . success
+```
+
+Instead, it looks like this:
+
+```html
+idle --(FETCH)--> loading --(RESOLVE)--> success
+```
+
+Everything is the result of some event triggering a state transition. Middleware obscures this fact. If you're curious how async cancellation can be handled in a synchronous state transition manner, here's a couple of guiding points for a potential implementation:
+
+- A cancellation intent is an _event_ (e.g., `{ type: 'CANCEL' }`)
+- Cancelling an in-flight request is an _action_ (i.e., side-effect)
+- "Canceled" is a state, whether it's a specific state (e.g., `canceled`) or a state where a request shouldn't be active (e.g., `idle`)
+
+## To be continued
+
+It is possible to model application state in Redux to be more like a finite state machine, and it is good to do so for many reasons. The applications that we write have different modes, or "behaviors", that vary depending on which "state" it's in. Before, this state might have been implicit. But now, with finite states, you can group behavior by these finite states (such as `idle`, `loading`, `success`, etc.), which makes the overall app logic much more clear, and prevents the app from getting stuck in an impossible state.
+
+Finite states also make clear what events can do, depending on which state it's in, as well as what all the possible states are in an application. Additionally, they can map one-to-one to views in user interfaces.
+
+But most importantly, state machines are present in all of the software that you write, and they have been for over half a century. Making finite state machines explicit brings clarity and robustness to complex app logic, and it is possible to implement them in any libraries that you use (or even no library at all).
+
+In the next post, we'll talk about how the Redux atomic global store is also half of a pattern, the challenges it presents, and how it compares to another well-known model of computation (the Actor model).
diff --git a/fumadocs/content/blog/2020-05-22-redux-is-half-a-pattern-2-2/index.mdx b/fumadocs/content/blog/2020-05-22-redux-is-half-a-pattern-2-2/index.mdx
new file mode 100644
index 000000000..470655986
--- /dev/null
+++ b/fumadocs/content/blog/2020-05-22-redux-is-half-a-pattern-2-2/index.mdx
@@ -0,0 +1,289 @@
+---
+title: Redux is half of a pattern (2/2)
+description: Learn how Redux, and all other state management libraries, have one thing in common - they are all partial implementations of state machines - and how we can improve the way we model app state and logic.
+tags: [redux, actor, react, state]
+date: 2020-05-22
+authors: [david]
+image: /blog/2020-05-22-redux-is-half-a-pattern-2-2.png
+slug: 2020-05-22-redux-is-half-a-pattern-2-2
+---
+
+
+
+
+
+I wrote a form library once.
+
+_Once._
+
+It was called [React Redux Form](https://github.com/davidkpiano/react-redux-form), and using Redux for forms was a good idea, at the time (don't use it). In fact, my library was written as a response to [Redux Form](https://github.com/redux-form/redux-form), and both libraries soon discovered that the idea of using a _single global store_ to store all of your application state is a really, really bad idea.
+
+When all of your forms live in one single store, state is easy to manage at first. And then, every single keypress starts to lag. It's a terrible user experience.
+
+{/* truncate */}
+
+So what do you do?
+
+- Blur inputs
+- Add debounced updates
+- Memoize _everything_
+- Optimize selectors everywhere
+- Make controlled components uncontrolled
+- Use `React.memo()` on components
+- Use `PureComponent` for good measure
+- Use Suspense (??)
+- etc. etc.
+
+In short, you go into panic mode and try to contain the spread of the global updates affecting every single connected component, even if those components don't need to rerender.
+
+Some of you have gotten really good at solving this, and have become expert "selector, caching, and memoization" developers. That's fantastic.
+
+But let's examine if those tactics should even be necessary. What if all state _wasn't_ global?
+
+
+
+## Local vs. global state
+
+The first of [Redux's three principles](https://redux.js.org/introduction/three-principles) is that there is essentially a _single source of truth_ for your whole application state:
+
+> The state of your whole application is stored in an object tree within a single store.
+>
+> _Source: [Redux's three principles: single source of truth](https://redux.js.org/introduction/three-principles#single-source-of-truth)_
+
+The primary reason for this is that it makes many things _easier_, such as sharing data, rehydrating state, "time-travel debugging", etc. But it suffers from a fundamental disconnect: _there is no such thing as a single source of truth_ in any non-trivial application. All applications, even front-end apps, are distributed at some level:
+
+
+
+And, in a contradictory way, even the Redux Style Guide advises against putting the entire state of your application in a single store:
+
+> [...] Instead, there should be a single place to find all values that you consider to be global and app-wide. Values that are "local" should generally be kept in the nearest UI component instead.
+>
+> _Source: [Redux Style Guide](https://redux.js.org/style-guide/style-guide/#evaluate-where-each-piece-of-state-should-live)_
+
+Whenever something is done for the sole purpose of making something easy, it almost always makes some other use-case more difficult. Redux and its single-source-of-truth is no exception, as there are many problems that arise from fighting against the nature of front-end apps being "distributed" instead of an idealistic atomic, global unit:
+
+- Multiple orthogonal concerns that need to be represented in the state somehow.
+
+This is "solved" by using [`combineReducers`](https://redux.js.org/recipes/structuring-reducers/using-combinereducers).
+
+- Multiple separate concerns that need to share data, communicate with each other, or are otherwise tangentially related.
+
+This is "solved" by [more complex, custom reducers](https://redux.js.org/recipes/structuring-reducers/beyond-combinereducers) that orchestrate events through these otherwise separate reducers.
+
+- Irrelevant state updates: when separate concerns are combined (using `combineReducers` or similar) into a single store, whenever any part of the state updates, the _entire_ state is updated, and every "connected" component (every subscriber to the Redux store) is notified.
+
+This is "solved" by using [selectors](https://redux.js.org/introduction/learning-resources#selectors), and perhaps by using another library like [`reselect`](https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/) for memoized selectors.
+
+I put "solved" in quotes because these are all solutions that are all but necessary due to problems that are caused solely by using a global, atomic store. In short, having a single global store is unrealistic, even for apps that are already using global stores. Whenever you use a 3rd-party component, or local state, or local storage, or query parameters, or a router, etc., you have already shattered the illusion of a single global store. App data is always distributed at some level, so the natural solution should be to embrace the distribution (by using local state) rather than fighting against it just for the sake of making some use-cases easier to develop in the short run.
+
+## Acting differently
+
+So how can we address this global state problem? To answer that, we need to go back in time a little bit and take some inspiration from another old, well-established model: [the actor model](https://en.wikipedia.org/wiki/Actor_model).
+
+The actor model is a surprisingly simple model that can be extended slightly beyond its original purpose (concurrent computation). In short, an actor is an entity that can do three things:
+
+- It can receive messages (events)
+- It can change its state/behavior as a reaction to a received message, including spawning other actors
+- It can send messages to other actors
+
+If you thought "hmm... so a Redux store is sort of an actor", congratulations, you already have a basic grasp of the model! A Redux store, which is based on some single combined-reducer thing:
+
+- ✅ Can receive events
+- ✅ Changes its state (and thus its behavior, [if you're doing it right](https://dev.to/davidkpiano/redux-is-half-of-a-pattern-1-2-1hd7)) as a reaction to those events
+- ❌ Can't send messages to other stores (there's only one store) or between reducers (dispatching only happens outside-in).
+
+It also can't really spawn other "actors", which makes the [Reddit example in the official Redux advanced tutorial](https://redux.js.org/advanced/async-actions) more awkward than it needs to be:
+
+```js
+function postsBySubreddit(state = {}, action) {
+ switch (action.type) {
+ case INVALIDATE_SUBREDDIT:
+ case RECEIVE_POSTS:
+ case REQUEST_POSTS:
+ return Object.assign({}, state, {
+ [action.subreddit]: posts(state[action.subreddit], action),
+ });
+ default:
+ return state;
+ }
+}
+```
+
+_Source: https://redux.js.org/advanced/async-actions#reducersjs_
+
+Let's dissect what is happening here:
+
+1. We're taking only the relevant slice of state we need (`state[action.subreddit]`), which should ideally be its own entity
+2. We are determining what the next state of only this slice should be, via `posts(state[action.subreddit], action)`
+3. We are surgically replacing that slice with the updated slice, via `Object.assign(...)`.
+
+In other words, there is no way we can dispatch or forward an event directly to a specific "entity" (or _actor_); we only have a single actor and have to manually update only the relevant part of it. Also, every other reducer in `combineReducers(...)` will get the entity-specific event, and even if they don't update, every single one of them will still be called for every single event. There's no easy way to optimize that. A function that isn't called is still much more optimal than a function that is called and ultimately does nothing (i.e., returns the same state), which happens most of the time in Redux.
+
+## Reducers and actors
+
+So how do reducers and actors fit together? Simply put, a reducer describes the behavior of an individual actor:
+
+- Events are sent to a reducer
+- A reducer's state/behavior can change due to a received event
+- A reducer can spawn actors and/or send messages to other actors (via executed declarative actions)
+
+This isn't a cutting-edge, groundbreaking model; in fact, you've probably been using the actor model (to some extent) without even knowing it! Consider a simple input component:
+
+```jsx
+const MyInput = ({ onChange, disabled }) => {
+ const [value, setValue] = useState('');
+
+ return (
+ setValue(e.target.value)}
+ onBlur={() => onChange(value)}
+ />
+ );
+};
+```
+
+This component, in an implicit way, is sort of like an actor!
+
+- It "receives events" using React's slightly awkward parent-to-child communication mechanism - prop updates
+- It changes state/behavior when an event is "received", such as when the `disabled` prop changes to `true` (which you can interpret as some event)
+- It can send events to other "actors", such as sending a "change" event to the parent by calling the `onChange` callback (again, using React's slightly awkward child-to-parent communication mechanism)
+- In theory, it can "spawn" other "actors" by rendering different components, each with their own local state.
+
+Reducers make the behavior and business logic more explicit, especially when "implicit events" become concrete, dispatched events:
+
+```jsx
+const inputReducer = (state, event) => {
+ /* ... */
+};
+
+const MyInput = ({ onChange, disabled }) => {
+ const [state, dispatch] = useReducer(inputReducer, {
+ value: '',
+ effects: [],
+ });
+
+ // Transform prop changes into events
+ useEffect(() => {
+ dispatch({ type: 'DISABLED', value: disabled });
+ }, [disabled]);
+
+ // Execute declarative effects
+ useEffect(() => {
+ state.effects.forEach((effect) => {
+ if (effect.type === 'notifyChange') {
+ // "Send" a message back up to the parent "actor"
+ onChange(state.value);
+ }
+ });
+ }, [state.effects]);
+
+ return (
+
+ dispatch({
+ type: 'CHANGE',
+ value: e.target.value,
+ })
+ }
+ onBlur={() => dispatch({ type: 'BLUR' })}
+ />
+ );
+};
+```
+
+## Multi-Redux?
+
+Again, one of Redux's three main principles is that Redux exists in a single, global, atomic source of truth. All of the events are routed through that store, and the single huge state object is updated and permeates through all connected components, which use their selectors and memoization and other tricks to ensure that they are only updated when they need to be, especially when dealing with excessive, irrelevant state updates.
+
+And using a single global store has worked pretty well when using Redux, right? Well... not exactly, to the point that there are entire libraries dedicated to providing the ability to use Redux on a more distributed level, e.g., for [component state and encapsulation](https://redux.js.org/introduction/ecosystem#component-state-and-encapsulation). It is possible to use Redux at a local component level, but that was not its main purpose, and the official `react-redux` integration does not naturally provide that ability.
+
+## No Redux?
+
+There are other libraries that embrace the idea of "state locality", such as [MobX](https://mobx.js.org/README.html) and [XState](https://xstate.js.org/docs/). For React specifically, there is [Recoil](http://recoiljs.org/) for "distributed" state and the built-in [`useReducer` hook](https://reactjs.org/docs/hooks-reference.html#usereducer) that feels a lot like a local Redux, specifically for your component. For declarative effects, I created [`useEffectReducer`](https://github.com/davidkpiano/useeffectreducer) which looks and feels just like `useReducer`, but also gives you a way to manage effects.
+
+For state that needs to be shared (not globally), you can use a pattern that is very similar to what React-Redux already uses, by making an object that can be subscribed to (i.e., "listened" to) and passed down through [context](https://reactjs.org/docs/hooks-reference.html#usecontext):
+
+
+
+That will give you the best performance, as that "subscribable" object will seldom/never change. If that feels a bit boilerplatey for you and performance is not a huge concern, you can combine `useContext` and `useReducer` with not too much effort:
+
+```jsx
+const CartContext = createContext();
+
+const cartReducer = (state, event) => {
+ // reducer logic
+ // try using a state machine here! they're pretty neat
+
+ return state;
+};
+
+const initialCartState = {
+ // ...
+};
+
+const CartContextProvider = ({ children }) => {
+ const [state, dispatch] = useReducer(cartReducer, initialCartState);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useCartContext = () => {
+ return useContext(CartContext);
+};
+```
+
+And then use it in your components:
+
+```jsx
+const CartView = () => {
+ const [state, dispatch] = useCartContext();
+
+ // ...
+};
+```
+
+Not too bad, right? In general, this is not a problem that can be solved in Redux without going against-the-grain, since Redux is fundamentally a single, atomic global store.
+
+## What do others think?
+
+I ran a non-scientific poll on Twitter to see where most app state lives, and how developers feel about it:
+
+
+
+
+
+From this, I gather two things:
+
+- Whether you distribute state locally, or contain all state in a single store, you will be able to accomplish app state requirements successfully.
+- However, more developers are discontent with the majority of app state being global instead of local, which also might hint to why the majority of developers are happy using local state instead.
+
+What do you think? Share your thoughts in the comments!
+
+## Conclusion
+
+Thinking in terms of "actors", in which your application is organized by lots of smaller actors that all talk to each other by passing messages/events to each other, can encourage separation of concerns and make you think differently about how state should be localized (distributed) and connected. My goal for this post is to help you realize that not _all_ state needs to be global, and that other patterns (such as the Actor Model) exist for modeling distributed state and communication flow.
+
+The Actor Model is not a panacea, though. If you're not careful, you can end up having a spaghetti-like state management problem, where you have completely lost track of which actor is talking to another actor. Anti-patterns are present in any solution that you choose, so it helps to research best practices and actually model your app before you start coding.
+
+If you want to learn more about the Actor Model, check out [The Actor Model in 10 Minutes](https://www.brianstorti.com/the-actor-model/) by [Brian Storti](https://twitter.com/brianstorti), or any of these videos:
+
+
+
+
+
+Please keep in mind that this is post reflects my opinions based on what I've researched, and is in no way meant to be authoritative on the way you should do things. I want to make you _think_, and I hope that this post accomplished that goal. Thanks for reading!
+
+If you enjoyed this post (or even if you didn't and just want to hear more of my state management ramblings), [subscribe to the Stately Newsletter](https://www.stately.dev/) for more content, thoughts, and discussion 📬
+
+
diff --git a/fumadocs/content/blog/2020-05-27-state-management-bad-bolean-good-boolean/index.mdx b/fumadocs/content/blog/2020-05-27-state-management-bad-bolean-good-boolean/index.mdx
new file mode 100644
index 000000000..da69b9aa3
--- /dev/null
+++ b/fumadocs/content/blog/2020-05-27-state-management-bad-bolean-good-boolean/index.mdx
@@ -0,0 +1,139 @@
+---
+title: 'How to tell a bad boolean from a good boolean'
+description: 'TL;DR: Bad booleans represent state. Good booleans are derived from state'
+tags: [tutorial]
+authors: [matt]
+category: entry
+image: /blog/2020-05-27-state-management-bad-bolean-good-boolean.png
+slug: 2020-05-27-state-management-bad-bolean-good-boolean
+date: 2020-05-27
+---
+
+**TL;DR**: Bad booleans represent state. Good booleans are derived from state.
+
+{/* truncate */}
+
+When you’re managing state in your app, it’s easy to fall prey to bad booleans. Bad booleans look like this:
+
+```js
+let isLoading = true;
+let isComplete = false;
+let hasErrored = false;
+```
+
+On the surface, this looks like good code. It appears as though you’ve represented three separate states with proper boolean names. In the ‘model’ you’ve pictured for your state, only one of these states can be true at any one time.
+
+In a fetch request, you might model the state like this:
+
+```js
+const makeFetch = async () => {
+ isLoading = true;
+ try {
+ await fetch('/users');
+
+ isComplete = true;
+ } catch (e) {
+ hasErrored = true;
+ }
+ isLoading = false;
+};
+```
+
+Again, this looks nice. We’re orchestrating our booleans as we move through the async request.
+
+But there’s a bug here. What happens if we make the fetch, it succeeds, and we make the fetch again? We’ll end up with:
+
+```js
+let isLoading = true;
+let isComplete = true;
+let hasErrored = false;
+```
+
+## Implicit states
+
+You probably hadn’t considered this when you made your initial model. You may have frontend components which are checking for `isComplete === ` true or `isLoading === true`. You might end up with a loading spinner and the previous data showing at the same time.
+
+How is this possible? Well, you’ve created some implicit states. Let’s imagine you considered 3 states as ones you actually wanted to handle:
+
+1. `loading`: Loading the data
+2. `complete`: Showing the data
+3. `errored`: Erroring if the data doesn't turn up
+
+Well, you’ve actually allowed 8 states! That’s 2 for the first boolean, times 2 for the second, times 2 for the third.
+
+This is what's known as boolean explosion - I learned about this from [Kyle Shevlin's egghead course](https://egghead.io/lessons/javascript-eliminate-boolean-explosion-by-enumerating-states).
+
+## Making states explicit
+
+How do you get around this? Instead of a system with 8 possible values, we need a system with three possible values. We can do this in Typescript with an enum.
+
+```ts
+type Status = 'loading' | 'complete' | 'errored';
+
+let status: Status = 'loading';
+```
+
+We’d implement this in a fetch like this:
+
+```ts
+const makeFetch = async () => {
+ status = 'loading';
+ try {
+ await fetch('/users');
+
+ status = 'complete';
+ } catch (e) {
+ status = 'errored';
+ }
+};
+```
+
+It’s now impossible to be in the 'loading' and 'complete' state at once - we’ve fixed our bug. We’ve turned our bad booleans into a good enum.
+
+## Making good booleans
+
+But not all booleans are bad. Many popular libraries, such as _react-query_, _apollo_ and _urql_ use booleans in their state. An example implementation:
+
+```js
+const [result] = useQuery();
+
+if (result.isLoading) {
+ return Loading...
;
+}
+```
+
+The reason these are good booleans is that their underlying mechanism is based on an enum. Bad booleans represent state. Good booleans are derived from state:
+
+```js
+let status: Status = 'loading';
+
+// Derived from the status above
+let isLoading = status === 'loading';
+```
+
+You can safely use this `isLoading` to display your loading spinner, happy in the knowledge that you've removed all impossible states.
+
+## Addendum: Enums in Javascript
+
+We can represent a state enum in Javascript as well. While the above code will work without typings, you can represent enums as an object type.
+
+```ts
+const statusEnum = {
+ loading: 'loading',
+ complete: 'complete',
+ errored: 'errored',
+};
+
+let status = statusEnum.loading;
+
+const makeFetch = async () => {
+ status = statusEnum.loading;
+ try {
+ await fetch('/users');
+
+ status = statusEnum.complete;
+ } catch (e) {
+ status = statusEnum.errored;
+ }
+};
+```
diff --git a/fumadocs/content/blog/2020-07-27-state-machines-how-to-stop-making-horcruxes-in-your-code/index.mdx b/fumadocs/content/blog/2020-07-27-state-machines-how-to-stop-making-horcruxes-in-your-code/index.mdx
new file mode 100644
index 000000000..04ffbb9db
--- /dev/null
+++ b/fumadocs/content/blog/2020-07-27-state-machines-how-to-stop-making-horcruxes-in-your-code/index.mdx
@@ -0,0 +1,116 @@
+---
+title: 'State machines: How to stop making Horcruxes in your code'
+description: >-
+ There are several ways of mitigating horcruxes. But visualisations can prevent
+ bugs, so why not go further?
+tags: [business logic, xstate, statechart, useReducer]
+authors: [matt]
+image: /blog/2020-07-27-state-machines-how-to-stop-making-horcruxes-in-your-code.png
+slug: 2020-07-27-state-machines-how-to-stop-making-horcruxes-in-your-code
+date: 2020-07-27
+---
+
+
+
+
+
+My code contains [Horcruxes](https://harrypotter.fandom.com/wiki/Horcrux). I’m not proud to admit it, since to make a Horcrux you need to commit a murder. The murders seemed intuitive at the time, and were usually for expedience. But nonetheless, I am a murderer.
+
+Let’s talk about how I got here.
+
+{/* truncate */}
+
+## The Soul of your app’s logic
+
+For an app to remain maintainable, its soul must remain intact. For many apps, logic is usually expressed in changes to state and behaviour.
+
+For instance, some logic for a modal might be expressed like this:
+
+```ts
+const modalReducer = (state = { isOpen: false }, action) => {
+ switch (action.type) {
+ case 'CLOSE':
+ return {
+ isOpen: false,
+ };
+ case 'OPEN':
+ return {
+ isOpen: true,
+ };
+ default:
+ return state;
+ }
+};
+```
+
+This is the reducer pattern, popularised by libraries like [Redux](https://redux.js.org/introduction/getting-started). In [React](https://reactjs.org/), we'd use this reducer in a `useReducer` hook:
+
+```ts
+const [state, dispatch] = useReducer(modalReducer);
+
+ dispatch({ type: 'OPEN' })}>Open Modal ;
+```
+
+If you want to know what happens when you dispatch the `OPEN` action, you only need look in one place: the `modalReducer`. This part of your app is easy to keep bug-free, because all the logic for how it works is contained in one place.
+
+But let’s say that requirements change. Now, before you open the modal you’ll need to fetch something from the API to check if you can open it. This seems intuitive enough:
+
+```ts
+ const [state, dispatch] = useReducer(modalReducer);
+
+ {
+ const canOpenTheModal = await checkIfUserCanOpenIt();
+ if (canOpenTheModal) {
+ dispatch({ type: 'OPEN' });
+ }
+ }>
+ Open Modal
+
+```
+
+The `onClick` handler is now making the API check to see if it should open the modal. If there’s a bug with the way the modal opens, there are now two places you need to check, the reducer and the event handler.
+
+The soul of your business logic has been split. Just like that, you made a Horcrux.
+
+## Going beyond usual evil
+
+The first Horcrux is never really the issue. You can survive one or two splits in your business logic. The trouble comes when you reach six or seven, split out over several 800-line files. I have wept over classes with dozens of methods, sequences with dozens of asynchronous steps.
+
+Logic split into more than one place cannot be visualised. If it can’t be visualised, it can’t be modified, deleted or extended safely.
+
+Let me make this plain: **Any code that cannot be concretely visualised will, at some point, result in a bug.**
+
+There are several ways of mitigating horcruxes. You can push to make your code more readable, writing clean event handlers which don’t contain any logic. You can try to colocate as much as your logic as possible, for instance using [ducks](https://github.com/erikras/ducks-modular-redux) patterns.
+
+But now we know that visualisation can prevent bugs, why not go further?
+
+## Statecharts
+
+I recently embarked on the most complex task I’d ever attempted as a developer - building a video chat app with multiple third-party integrations. Most of the business logic would take place on a single page - the chat portal. I needed to handle multiple user types, chat, notifications, input changes... And many more things.
+
+I’d been using [XState](https://github.com/davidkpiano/xstate), and read that it could be used for visualising complex behaviours, such as Ryan Florence’s checkout:
+
+
+
+It produces clickable visualisations which you can share and walk through. For instance, the modal reducer above could be expressed in [this statechart](https://xstate.js.org/viz/?gist=56c56d2be397c4d5bea934c6d89e788d).
+
+
+
+I knew that if I wasn’t careful, I’d end up with 25-30 horcruxes under my belt, logic carried in dozens of event handlers. So I wrote the machine in XState. I even wrote a VSCode extension so I could have the visualiser inline as I went along. Here’s what I ended up with:
+
+
+
+Every part of this crazy complex app can now be visualised. I can see which conditions lead to which states. I can which actions are called on which events.
+
+At an early stage of the process, I was even able to ask the client about complex interactions in the logic, just by walking them through the statechart. Weeks later, when it needed extending, I was able to insert a new piece of state without breaking anything else.
+
+Any time someone needs to see how the viewing page works, they can fire up the visualiser. Their usual first reaction is one of terror. State machines don't create complexity, they reveal it. But by clicking through the machine, they can concretely see how the app works, and feel comfortable making changes.
+
+## Make a bet on XState
+
+---
+
+If you’re having trouble deciphering the logic of a legacy app, try visualising it in XState. Try working through a problem with a fellow dev. At [Yozobi](http://yozobi.com/), we’re making a bet that state machines are going to change web development. If you want to learn more about them, follow me and we can try to heal our Horcruxes together.
diff --git a/fumadocs/content/blog/2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate/index.mdx b/fumadocs/content/blog/2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate/index.mdx
new file mode 100644
index 000000000..4e8326d64
--- /dev/null
+++ b/fumadocs/content/blog/2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate/index.mdx
@@ -0,0 +1,259 @@
+---
+title: '“Just use props”: An opinionated guide to React and XState'
+description: >-
+ XState is the most powerful tool available for managing complex state. The
+ challenge comes when integrating XState with React…
+tags: [prop, xstate, state machine, react]
+authors: [matt]
+image: /blog/2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate.png
+slug: 2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate
+date: 2021-01-11
+---
+
+XState can feel overwhelming. Once you’ve gone through [Kyle](https://egghead.io/courses/introduction-to-state-machines-using-xstate) or [David’s](https://frontendmasters.com/courses/xstate/) courses and read through the [docs](https://xstate.js.org/docs), you’ll get a thorough understanding of the API. You’ll see that XState is the most powerful tool available for managing complex state.
+
+The challenge comes when integrating XState with React. Where should state machines live in my React tree? How should I manage parent and child machines?
+
+{/* truncate */}
+
+
+
+## Just Use Props
+
+I’d like to propose an architecture for XState and React which prioritises simplicity, readability and type-safety. It’s incrementally adoptable, and gives you a base for exploring more complex solutions. We’ve used it at [Yozobi](https://www.yozobi.com/) in production, and we’re planning to use it for every project moving forward.
+
+It’s called **just use props**. It’s got a few simple rules:
+
+1. Create machines. Not too many. Mostly useMachine
+2. Let React handle the tree
+3. Keep state as local as possible
+
+### Create machines. Not too many. Mostly useMachine
+
+The simplest way to integrate a state machine in your app is with `useMachine`.
+
+```ts
+import { createMachine, interpret } from 'xstate';
+import { useMachine } from '@xstate/react';
+
+const machine = createMachine({
+ initial: 'open',
+ states: {
+ open: {},
+ closed: {},
+ },
+});
+
+const Component = () => {
+ const [state, send] = useMachine(machine);
+
+ return state.matches('open') ? 'Open' : 'Closed';
+};
+```
+
+Note that this puts React in charge of the machine. The machine is tied to the component, and it obeys all the normal React rules of the [data flowing down](https://reactjs.org/docs/state-and-lifecycle.html#the-data-flows-down). In other words, you can think of it just like `useState` or `useReducer`, but a [vastly improved version](https://dev.to/mpocock1/usestate-vs-usereducer-vs-xstate-part-1-modals-569e).
+
+### Let React handle the tree
+
+Let’s say you have a parent component and a child component. The parent has some state which it needs to pass to the child. There are several ways to do this.
+
+#### Passing services through props
+
+The first is to pass a running service to the child which the child can subscribe to:
+
+```ts
+import { useMachine, useService } from '@xstate/react';
+import { createMachine, Interpreter } from 'xstate';
+
+/**
+ * Types for the machine declaration
+ */
+type MachineContext = {};
+type MachineEvent = { type: 'TOGGLE' };
+
+const machine = createMachine({});
+
+const ParentComponent = () => {
+ /**
+ * We instantiate the service here...
+ */
+ const [state, send, service] = useMachine(machine);
+
+ return ;
+};
+
+interface ChildComponentProps {
+ service: Interpreter;
+}
+
+const ChildComponent = (props: ChildComponentProps) => {
+ /**
+ * ...and receive it here
+ */
+ const [state, send] = useService(props.service);
+
+ return (
+ send('TOGGLE')}>
+ {state.matches('open') ? 'Open' : 'Closed'}
+
+ );
+};
+```
+
+I don’t like this pattern. For someone not used to XState, it’s unclear what a ‘service’ is. We don’t get clarity from reading the types, which is a particularly ugly `Interpreter` with multiple generics.
+
+The machine appears to bleed across multiple components. Its service seems to have a life of its own, outside of React's tree. To a newbie, this feels like misdirection.
+
+#### Just pass props
+
+This can be expressed much more cleanly using props:
+
+```ts
+import { useMachine } from '@xstate/react';
+import { createMachine } from 'xstate';
+
+/**
+ * Types for the machine declaration
+ */
+type MachineContext = {};
+type MachineEvent = { type: 'TOGGLE' };
+
+const machine = createMachine({});
+
+const ParentComponent = () => {
+ const [state, send] = useMachine(machine);
+
+ return (
+ send('TOGGLE')}
+ />
+ );
+};
+
+/**
+ * Note that the props declarations are
+ * much more specific
+ */
+interface ChildComponentProps {
+ isOpen: boolean;
+ toggle: () => void;
+}
+
+const ChildComponent = (props: ChildComponentProps) => {
+ return (
+ props.toggle()}>
+ {props.isOpen ? 'Open' : 'Closed'}
+
+ );
+};
+```
+
+Much better. We get several improvements in clarity in the `ChildComponent` - the types are much easier to read. We get to ditch the use of `Interpreter` and `useService` entirely.
+
+The best improvement, though, is in the `ParentComponent`. In the previous example, the machine crossed multiple components by passing its service around. In this example, it’s scoped to the component, and props are derived from its state. This is far easier to grok for someone unused to XState.
+
+### Keep state as local as possible
+
+Unlike tools which require a global store, XState has no opinion on where you keep your state. If you have a piece of state which belongs near the root of your app, you can use React Context to make it globally available:
+
+```ts
+import React, { createContext } from 'react';
+import { useMachine } from '@xstate/react';
+import { createMachine } from 'xstate';
+
+const globalMachine = createMachine({});
+
+interface GlobalContextType {
+ isOpen: boolean;
+ toggle: () => void;
+}
+
+export const GlobalContext = createContext();
+
+const Provider: React.FC = ({ children }) => {
+ const [state, send] = useMachine(globalMachine);
+
+ return (
+ send('TOGGLE') }}
+ >
+ {children}
+
+ );
+};
+```
+
+_Just as above, we’re not passing a service, but props, into context._
+
+If you have a piece of state which needs to belong lower in your tree, then obey the usual rules by [lifting state up](https://reactjs.org/docs/lifting-state-up.html) to where it’s needed.
+
+If that feels familiar, you’re right. You’re making the same decisions you’re used to: where to store state and how to pass it around.
+
+## Examples and challenges
+
+### Syncing parents and children
+
+Sometimes, you need to use a parent machine _and_ a child machine. Let’s say that you need the child to pay attention to when a prop changes from the parent - for instance to sync some data. Here’s how you can do it:
+
+```ts
+const machine = createMachine({
+ initial: 'open',
+ context: {
+ numberToStore: 0,
+ },
+ on: {
+ /**
+ * When REPORT_NEW_NUMBER occurs, sync
+ * the new number to context
+ */
+ REPORT_NEW_NUMBER: {
+ actions: [
+ assign((context, event) => {
+ return {
+ numberToStore: event.newNumber,
+ };
+ }),
+ ],
+ },
+ },
+});
+
+interface ChildComponentProps {
+ someNumber: number;
+}
+
+const ChildComponent = (props: ChildComponentProps) => {
+ const [state, send] = useMachine(machine);
+
+ useEffect(() => {
+ send({
+ type: 'REPORT_NEW_NUMBER',
+ newNumber: props.someNumber,
+ });
+ }, [props.someNumber]);
+};
+```
+
+This can also be used to sync data from other sources, such as query hooks:
+
+```ts
+const ChildComponent = () => {
+ const [result] = useSomeDataHook(() => fetchNumber());
+
+ const [state, send] = useMachine(machine);
+
+ useEffect(() => {
+ send({
+ type: 'REPORT_NEW_NUMBER',
+ newNumber: result.data.someNumber,
+ });
+ }, [result.data.someNumber]);
+};
+```
+
+## Summary
+
+In the “just use props” approach, XState lets React take charge. We stick to idiomatic React by passing props, not services. We keep machines scoped to components. And we put state at the level it’s needed, just like you’re used to.
+
+This article isn’t finished. I’m sure there will be many more questions about integrating XState with React. My plan is to come back to this article again with more examples and clarifications. Thanks for your time, and I’m looking forward to seeing what you build with XState.
diff --git a/fumadocs/content/blog/2021-01-20-you-dont-need-a-library-for-state-machines/index.mdx b/fumadocs/content/blog/2021-01-20-you-dont-need-a-library-for-state-machines/index.mdx
new file mode 100644
index 000000000..79c4ed08d
--- /dev/null
+++ b/fumadocs/content/blog/2021-01-20-you-dont-need-a-library-for-state-machines/index.mdx
@@ -0,0 +1,382 @@
+---
+title: You don’t need a library for state machines
+description: >-
+ Do you need a library to create and interpret state machines in your programs?
+ No. But there are more things to consider.
+tags: [finite state machine, statechart, xstate, state machine]
+authors: [david]
+image: /blog/2021-01-20-you-dont-need-a-library-for-state-machines.png
+slug: 2021-01-20-you-dont-need-a-library-for-state-machines
+date: 2021-01-20
+---
+
+
+
+
+
+The finite state machine is one of the oldest models of computation in computer science. It’s older than the web, older than any programming language you can think of, and probably older than you. Just ask [Mealy (1955)](https://en.wikipedia.org/wiki/Mealy_machine) or [Moore (1956)](https://en.wikipedia.org/wiki/Moore_machine). Finite state machines (FSMs) can be implemented in any modern language using control-flow statements, yet there’s most likely a state machine library (if not many) in all of those languages.
+
+{/* truncate */}
+
+So do you need a library to create and interpret state machines in your programs?
+
+**No.** But there are more things to consider.
+
+## You probably need state machines
+
+If you’re unfamiliar with [finite state machines (FSMs)](https://en.wikipedia.org/wiki/Finite-state_machine), they are a visual and mathematical way of modeling stateful logic using 3 main building blocks:
+
+- **Finite states**, which represent different _behaviors_
+- **Events**, which represent something that happened that can change state
+- **Transitions**, which represent how the state can change and what actions are executed when an event is received
+
+
+
+Anything that can be described as changes in state over time due to events, from component-specific logic to application flows and even the orchestration of multiple services can be described with state machines, to some extent.
+
+A state machine might be a different, unfamiliar way of thinking about your application logic, but they’re extremely useful. Instead of approaching logic from a "bottom-up" perspective (imperatively doing things based on events), they take a "top-down" approach and primarily consider _behaviors_, which describe how the logic will react to events in a given finite state (such as `loading`, `editing`, `disabled`, etc.).
+
+Because of their explicit, declarative nature, state machines force you to think about the entire flow of your logic (including all the edge-cases), and make it virtually impossible to end up in an ”impossible state”, as long as your model doesn’t allow it. Only defined transitions can happen; and if an unexpected transition happens, it means there is an implicit state machine where that transition _does_ exist. The goal of state machines is to eliminate the implicit transitions so that we can know exactly what can happen in any state for any potential event.
+
+State machines are **not a solution for everything** - just like anything else, they make sense for some use-cases (workflows, processes, modes, statuses, etc.) but not all use-cases. You shouldn’t use state machines everywhere, or even implement them explicitly all of the time (that’s what abstractions are for). They make a good refactor target, and they’re great for visually modeling your logic with pencil and paper, even if you ultimately decide not to use them in your code. But when working with logic that deals with explicit states, events, and transitions (which, surprise, tends to be the majority of app logic), state machines are a brilliant, natural solution.
+
+There are so many other benefits to thinking in terms of states, events, and transitions, but that’s not the point of this post (but it is the point of [another post I wrote](https://dev.to/davidkpiano/no-disabling-a-button-is-not-app-logic-598i)). Let’s say you’re already convinced in using state machines in parts of your app. Should you reach for a library?
+
+## You don’t need a library for state machines
+
+Since state machines are not a new concept and can be implemented in any modern language using built-in language features, it follows that state machine libraries are not necessary. Again, all you need are the 3 building blocks:
+
+- **Finite states**
+- **Events**
+- **Transitions**
+
+The transitions are what tie everything together. Transitions are represented by a [state-transition function](https://en.wikipedia.org/wiki/Finite-state_machine#Mathematical_model) that looks like this, mathematically:
+
+> 𝛅 : 𝑆 𝗑 𝛴 → 𝑆
+
+…which might not make sense (even if you do speak Greek). This might be more understandable:
+
+> transition : (state, event) => nextState
+
+In JavaScript, we can represent this as a _reducer_, which is a function that reduces values (events) to a single accumulated value (state):
+
+```js
+function transition(state, event) {
+ // state machine goes here, which
+ // determines the next state based on the
+ // current state + received event
+ // ...
+
+ return nextState;
+}
+```
+
+Now, let’s draw the rest of the owl implement the rest of the state machine!
+
+### Using `switch` statements
+
+Typically, when we’re determining _behavior_ ("what happens next"), we tend to decide what should happen next based on the _event_. The finite state is an after-thought, if it’s even a consideration at all. This leads to fragile logic, with `if`\-statements strewn all over the place:
+
+```js
+ // ❌ Event-first approach
+ switch (event.type) {
+ case 'DATA_RECEIVED':
+ // defensive programming
+ if (state.isLoading) {
+ // do something
+ } else {
+ // ...
+ }
+ }
+ // ...
+ }
+```
+
+In contrast, state machines group behavior by **finite state** and narrow down what happens next based on the event received:
+
+```js
+// ✅ Finite-state-first approach
+switch (state.status) {
+ case 'loading':
+ // narrow based on event
+ switch (event.type) {
+ case 'DATA_RECEIVED':
+ // do something, and possibly
+ // change the finite state
+ // ...
+ }
+ // ...
+}
+```
+
+As the author of the code, the event-first (bottom-up) approach might seem fine to you; after all, if it works, it works. One of the main advantages of taking a ”finite-state-first” (top-down) approach and using state machines is that the logic is not only more clear (since it’s grouped by finite state), it’s more robust: you can ensure that an event won’t be improperly handled in a state that it shouldn’t be handled in. In other words, you prevent _impossible states_ and _impossible transitions_ without having to litter your code with `if`\-statements and excessive defensive programming.
+
+I also like to think of state machines as a formal way of communicating logic. If you were describing the above logic, here’s how it would sound with an event-first approach:
+
+> When data is received, do something, but only if the "loading" flag is true.
+
+And with a finite-state-first approach:
+
+> In the ”loading” state, when data is received, do something.
+
+Which one sounds more natural and easy to understand? To me, there is less cognitive load with the 2nd statement. Reactions to events are grouped by _behavior_ (finite state) rather than being ungrouped.
+
+### Using `switch` statements with functions
+
+Since finite states can be considered a way to group behavior, another way you can organize your `switch` statements is by “grouping” each finite state’s behavior into a function:
+
+```js
+ // 'loading' behavior
+ function loadingState(state, event) {
+ // switch only on the event
+ switch (event.type) {
+ case 'DATA_RECEIVED':
+ return {
+ ...state,
+ status: 'success'
+ }
+ }
+ // ...
+ }
+ }
+
+ function dataMachine(state, event) {
+ switch (state.status) {
+ case 'loading':
+ // handle the event with 'loading' behavior
+ return loadingState(state, event);
+ }
+ // ...
+ }
+ }
+```
+
+This approach is outlined in the [Redux style guide recommendation: Treat Reducers as State Machines](https://redux.js.org/style-guide/style-guide/#treat-reducers-as-state-machines). It’s a very organized approach, and each “behavior function” can be individually tested, since they are isolated, pure reducers.
+
+### Using objects
+
+Using nested `switch` statements may feel verbose, and while using functions to organize these `switch` statements may look cleaner, it’s more tedious. After all, a state transition can be considered a configuration of (at least) 2 things based on the event received:
+
+- The next **finite state**, if it changes
+- Any **action(s)** executed, if any
+
+A simple, built-in way to represent such a configuration is an object. We can create an object structure where each ”state node” represents a finite state with transitions for each event accepted by the state:
+
+```js
+const machine = {
+ initial: 'loading',
+ states: {
+ // A finite "state node"
+ loading: {
+ on: {
+ // event types
+ DATA_RECEIVED: {
+ target: 'success',
+ // actions: [...]
+ },
+ },
+ },
+ // ...
+ },
+};
+// ...
+```
+
+This is much more succinct than the nested `switch` statements! From here, determining the next state based on the current finite state and received event is two key lookups (the finite state and the event type):
+
+```js
+// ...
+function transition(state, event) {
+ const nextStateNode = // lookup next finite state based on event type
+ // lookup configuration for current finite state
+ machine.states[state.status].on?.[event.type] ?? { target: state.status }; // if not handled, stay on current state
+
+ return {
+ ...state,
+ status: nextStateNode.target,
+ };
+}
+
+transition({ status: 'loading' }, { type: 'DATA_RECEIVED' });
+// => { status: 'success', ... }
+```
+
+You might be wondering why I didn’t use an even simpler object here, which you definitely can do:
+
+```js
+const transitions = {
+ loading: {
+ DATA_RECEIVED: 'success',
+ },
+ success: {
+ /* ... */
+ },
+};
+
+function transition(state, event) {
+ const nextStateTarget = transitions[state.status][event.type] ?? state.status;
+
+ return {
+ ...state,
+ status: nextStateTarget,
+ };
+}
+```
+
+In fact, I would encourage the above implementation as sort of a “transition table lookup”; it works, and it’s simple enough. However, state machines deal with more than just the next finite state; if we want to encode **actions** (state machine terminology for effects), we need a place to put them, so a little bit more structure is necessary.
+
+For instance, if our `DATA_RECEIVED` event returns data that we want to save in our overall state, it might be convenient to place that “assign to state” action directly in the machine:
+
+```js
+const machine = {
+ initial: 'loading',
+ states: {
+ loading: {
+ on: {
+ // event types
+ DATA_RECEIVED: {
+ target: 'success',
+ // represents what "effects" should happen
+ // as a result of taking this transition
+ actions: [{ type: 'saveData' }],
+ },
+ },
+ },
+ // ...
+ },
+};
+
+function transition(state, event) {
+ const nextStateNode = machine.states[state.status].on?.[event.type] ?? {
+ target: state.status,
+ };
+
+ const nextState = {
+ ...state,
+ status: nextStateNode.target,
+ };
+
+ // go through the actions to determine
+ // what should be done
+ nextStateNode.actions?.forEach((action) => {
+ if (action.type === 'saveData') {
+ nextState.data = event.data;
+ }
+ });
+
+ return nextState;
+}
+```
+
+The implementation above is very small, accomplishes everything we want from a state machine (for this use-case, at least), and as a bonus, you can copy-paste the `machine` object code directly into the [XState Visualizer](https://xstate.js.org/viz), even though it's not using XState, or any libraries, at all! (Tip: wrap the object in `Machine({ ... })` to get it working).
+
+Kent C. Dodds made a similar implementation is his post [Implementing a Simple State Machine Library in JavaScript](https://kentcdodds.com/blog/implementing-a-simple-state-machine-library-in-javascript). It also takes advantage of using objects for describing the state machine structure.
+
+## State machines aren't enough
+
+So if we can get our basic state management needs met with a small, declarative, library-free state machine implementation (either using `switch` statements or objects), why do we need libraries such as [XState](https://github.com/davidkpiano/xstate)?
+
+This might be a bit of a shock coming from me, but I’ll say it: _state machines are not sufficient_ for managing and orchestrating state at scale. State machines suffer from a fundamental problem called [state explosion](https://statecharts.github.io/state-machine-state-explosion.html): when the number of states in a state machine grow, the transitions between states also tend to grow, _exponentially_.
+
+Thankfully, an extension to the traditional formalism of state machines, known as **statecharts**, was invented by Prof. David Harel and published in his paper [Statecharts: A Visual Formalism for Complex Systems](https://www.sciencedirect.com/science/article/pii/0167642387900359/pdf). The paper is full of diagrams and is quite readable; I strongly encourage you to read it.
+
+You can think of statecharts as essentially being state machines (statecharts can be decomposed into FSMs) with some essential features for better state organization and real-world use-cases:
+
+- **Hierarchy** (nested states)
+- **Orthogonality** (parallel states)
+- **History** (remembered states)
+- **State actions** (entry, exit)
+- **Guarded transitions**
+- **Extended state** (contextual data)
+
+Notably, the first two features (hierarchy and orthogonality) mitigate the state explosion problem by allowing state nodes to be grouped in a way that reduces the number of transitions necessary to fully express all possible transitions.
+
+For example, if you were creating a state machine to represent editing and asynchronously saving some data, and you wanted to have shared behavior between some ”idle” (before saving) and “error” (failure after saving) state (e.g., `SUBMIT` to try/retry), then instead of having a flat state machine:
+
+```js
+ {
+ idleNormal: {
+ on: {
+ SAVE: {
+ target: 'saving',
+ actions: [{ type: 'saveAsync' }]
+ }
+ }
+ },
+ saving: {/* ... */},
+ idleError: {
+ on: {
+ SAVE: {
+ target: 'saving',
+ actions: [{ type: 'saveAsync' }]
+ }
+ }
+ },
+ // ...
+ }
+
+```
+
+You can represent the shared behavior under the same parent state:
+
+```js
+ {
+ idle: {
+ // if child states don't handle these events,
+ // handle it here, in the parent state
+ on: {
+ SAVE: {
+ target: 'saving',
+ actions: [{ type: 'saveAsync' }]
+ }
+ },
+ initial: 'normal',
+ states: {
+ normal: {/* ... */},
+ error: {/* ... */}
+ }
+ },
+ saving: {/* ... */},
+ // ...
+ }
+```
+
+Overall, the features of statecharts are very useful in many different situations:
+
+- **Nested states** are useful for grouping and refining behavior. Different ”finite states” can all share behavior, while all having their own specific behavior.
+- **Parallel states** are useful for representing behaviors that can occur simultaneously, without directly affecting each other.
+- **History states** are useful for recalling which nested state the machine was previously in without having to specify all the possible ”remembering” transitions.
+- **State actions** are useful for specifying actions that should always be executed on any transition that enters/exits a state without having to specify those actions in all incoming/outgoing transitions.
+- **Guarded transitions** are very important for conditionally taking transitions based on more than just the state and event type. They can take other data (extended state) and/or event data into consideration, as well.
+- **Extended state** is absolutely necessary. Not all state is finite; ”infinite” state also needs to be quantified. Statecharts allow you to distinguish between finite and extended state.
+
+There's even more features of classic statecharts, such as ”activities” (actions that occur _throughout_ a state), delays, eventless transitions, wildcard transitions, and more. And the more you work with statecharts, the more you realize just how essential most of these features actually are.
+
+Sounds like it would be fun to implement these features on top of our state machines, right?
+
+## Implementing statecharts
+
+I hope you have a _lot_ of free time.
+
+Since statecharts are more powerful than state machines, they’re also harder to implement. If you’re really curious and/or eager to implement them yourself, I strongly recommend following the [W3 SCXML (Statechart XML) spec](https://www.w3.org/TR/scxml). They even include [an algorithm in pseudocode](https://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation) for proper SCXML interpretation.
+
+Even implementing something as seemingly straightforward as nested states is a daunting task. There are many rules about selecting transitions, resolving conflicting transitions, traversing the state node tree to determine which nodes are being exited/entered, selecting transitions in compound states if leaf nodes don't handle the event, determining action order, etc. etc.
+
+It’s not easy, and just like you would use a date library to deal with timezones, you definitely want to use a statechart library to deal with all the excellent features that statecharts support.
+
+## So do you need a library for statecharts?
+
+Yes.
+
+## Closing thoughts
+
+If you’re satisfied manipulating state at any time and sprinkling `if`\-statements to patch up edge-cases, you probably don’t need explicit state machines.
+
+If you want to use simple state machines to help organize app behavior and logic, you don’t need a library.
+
+If you have complex logic and want to take advantage of more powerful state machine features to better manage this logic, you need statecharts.
+
+And you _definitely_ need a library for statecharts. 😉
diff --git a/fumadocs/content/blog/2021-04-28-whats-the-difference-between-machine-and-createmachine/index.mdx b/fumadocs/content/blog/2021-04-28-whats-the-difference-between-machine-and-createmachine/index.mdx
new file mode 100644
index 000000000..2dfd5c64f
--- /dev/null
+++ b/fumadocs/content/blog/2021-04-28-whats-the-difference-between-machine-and-createmachine/index.mdx
@@ -0,0 +1,77 @@
+---
+title: What’s the difference between Machine and createMachine?
+description: >-
+ XState offers two options for declaring machine definitions. This can be
+ confusing for beginners. Why are there two very similar-looking methods?
+ What’s the difference?
+tags: [typescript, state machine]
+authors: [matt]
+image: /blog/2021-04-28-whats-the-difference-between-machine-and-createmachine.png
+slug: 2021-04-28-whats-the-difference-between-machine-and-createmachine
+date: 2021-04-28
+---
+
+
+
+
+
+XState offers two options for declaring machine definitions:
+
+```ts
+import { Machine } from 'xstate';
+
+const machine = Machine({ ...config });
+```
+
+…or…
+
+```ts
+import { createMachine } from 'xstate';
+
+const machine = createMachine({ ...config });
+```
+
+This can be confusing for beginners. Why are there two very similar-looking methods? What’s the difference?
+
+{/* truncate */}
+
+## The Difference
+
+In Javascript, there is no difference between the two. You can use them completely interchangeably.
+
+In Typescript, there is only a small difference between them - it’s to do with the ordering of the generics you can pass to the machine. `Machine` allows you to pass a generic called ['Typestates'](https://xstate.js.org/docs/guides/typescript.html#typestates) in the middle of the `Context` and `Event` generics.
+
+```ts
+import { Machine } from 'xstate';
+
+interface Context {}
+
+type Event = { type: 'EVENT_NAME' };
+
+type States = {};
+
+const machine = Machine({ ...config });
+```
+
+Whereas `createMachine` asks you to insert it at the end:
+
+```ts
+import { createMachine } from 'xstate';
+
+interface Context {}
+
+type Event = { type: 'EVENT_NAME' };
+
+type States = {};
+
+const machine = createMachine({ ...config });
+```
+
+Whichever you choose, there is _no functional difference in the created machine_. The two functions reference the same code, and create the machine in the same way.
+
+## What should I choose?
+
+Going forward, you should use `createMachine`. That’s the syntax that will be preferred when v5 releases. But if you're happy with Machine, you can keep using it.
diff --git a/fumadocs/content/blog/2021-04-29-should-this-be-a-state-or-in-context/index.mdx b/fumadocs/content/blog/2021-04-29-should-this-be-a-state-or-in-context/index.mdx
new file mode 100644
index 000000000..89f833dc7
--- /dev/null
+++ b/fumadocs/content/blog/2021-04-29-should-this-be-a-state-or-in-context/index.mdx
@@ -0,0 +1,154 @@
+---
+title: 'Should this be a state, or in context?'
+description: How to decide when to use state or context.
+tags: [context, state machine, xstate, state]
+authors: [matt]
+image: /blog/2021-04-29-should-this-be-a-state-or-in-context.png
+slug: 2021-04-29-should-this-be-a-state-or-in-context
+date: 2021-04-29
+---
+
+State machines offer several API’s for expressing state. Like other tools, you can keep arbitrary values in a store (usually expressed as an object) called `context`.
+
+{/* truncate */}
+
+This is handy for values which change over time and you need to keep updated, like the value of a form input:
+
+```ts
+import { createMachine, assign } from 'xstate';
+
+const machine = createMachine({
+ context: {
+ name: '',
+ },
+ on: {
+ CHANGE_NAME: {
+ actions: assign((context, event) => {
+ return {
+ name: event.value,
+ };
+ }),
+ },
+ },
+});
+```
+
+Every time the `CHANGE_NAME` event is sent to the machine, we'll update the value in `context`. We can then use that value to display the value in our UI or send it to an API.
+
+XState also gives you another way of expressing state - through finite states. Let's imagine a modal:
+
+```ts
+const machine = createMachine({
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: 'open',
+ },
+ },
+ open: {
+ on: {
+ CLOSE: 'close',
+ },
+ },
+ },
+});
+```
+
+Here, the modal's state is expressed through the `states: {}` attribute, which also defines which events can be received during each state. You can only `CLOSE` the modal when it's `open`, and vice versa.
+
+## Which should I choose?
+
+The choice between using `context` and `states` isn't always clear. For instance, the modal machine above could be expressed using `context`:
+
+```ts
+const machine = createMachine({
+ context: {
+ isOpen: false,
+ },
+ on: {
+ OPEN: {
+ actions: assign({ isOpen: true }),
+ },
+ CLOSE: {
+ actions: assign({ isOpen: false }),
+ },
+ },
+});
+```
+
+This gives you exactly the same functionality as the states-based one above - you can track when the modal is open and closed, and send the same events.
+
+The reason this can be expressed using both `states` and `context` is because _all of the events do the same thing no matter what state you’re in_. There are no events you need to declare as impossible in certain states.
+
+To show you what I mean, let’s imagine a form input inside a modal. We only want to allow changes to the form input while the modal is open.
+
+```ts
+const machine = createMachine({
+ initial: 'closed',
+ context: {
+ name: '',
+ },
+ states: {
+ closed: {
+ on: {
+ OPEN: 'open',
+ },
+ },
+ open: {
+ on: {
+ CLOSE: 'close',
+ CHANGE_NAME: {
+ actions: assign((context, event) => {
+ return {
+ name: event.value,
+ };
+ }),
+ },
+ },
+ },
+ },
+});
+```
+
+When the modal is in the `closed` state, the `CHANGE_NAME` event will not change the value in `context`. State machines are great at this - only allowing the things you want to happen to happen. Some other examples might be:
+
+- Not allowing users to submit a form while the previous API call is loading
+- Only allowing users to log in if they’re not already logged in
+
+## Putting things in context
+
+You might be wondering - but, I _can_ express the above in `context`!
+
+```ts
+const machine = createMachine({
+ context: {
+ name: '',
+ isOpen: false,
+ },
+ on: {
+ OPEN: { actions: assign({ isOpen: true }) },
+ CLOSE: { actions: assign({ isOpen: false }) },
+ CHANGE_NAME: {
+ actions: assign((context, event) => {
+ // This acts as the guard to prevent editing
+ // the name while it's open
+ if (!context.isOpen) return {};
+ return {
+ name: event.value,
+ };
+ }),
+ },
+ },
+});
+```
+
+I think this is incorrect for two reasons. First, as requirements grow, so will the complexity of your logic. Let’s imagine that the modal can now be either `closing` (i.e. animating out) or `closed`. We’ll soon see an explosion of booleans, as I discussed in [this article on useState/useReducer](https://dev.to/mpocock1/usestate-vs-usereducer-vs-xstate-part-1-modals-569e).
+
+Second, XState is auto-documenting via the [XState visualiser](https://xstate.js.org/viz/). The more your logic is expressed in `states`, the easier it’s going to be to visualise. The machine above is basically a single state with its logic expressed in ways that XState can’t visualise.
+
+## Rules to live by
+
+You should be keeping most of your state in context. That includes form values, API data - anything which cannot be expressed finitely.
+
+But state machines are powerful _because_ of their states. Use states when you want to express your logic visually, or gate events to certain states.
diff --git a/fumadocs/content/blog/2021-04-30-should-this-be-an-action-or-a-service/index.mdx b/fumadocs/content/blog/2021-04-30-should-this-be-an-action-or-a-service/index.mdx
new file mode 100644
index 000000000..045079eb4
--- /dev/null
+++ b/fumadocs/content/blog/2021-04-30-should-this-be-an-action-or-a-service/index.mdx
@@ -0,0 +1,218 @@
+---
+title: 'Should this be an action, or a service?'
+description: Whether to use an action or a service in XState.
+tags: [action, state machine, xstate, service]
+authors: [matt]
+image: /blog/2021-04-30-should-this-be-an-action-or-a-service.png
+slug: 2021-04-30-should-this-be-an-action-or-a-service
+date: 2021-04-30
+---
+
+XState offers several ways of orchestrating side effects. Since it’s a statechart tool, [with significantly more power than a reducer](https://dev.to/mpocock1/usestate-vs-usereducer-vs-xstate-part-1-modals-569e), side effects are treated as a first-class concept.
+
+{/* truncate */}
+
+‘Side effects’ as a concept spring from the idea of ‘pure’ functions. I.e. a function is at its best when it takes an input and returns an output. Pure functions don’t tend to involve:
+
+- Waiting a set amount of time
+- Making an API call to an external service
+- Logging things to an external service
+
+So we can think of all of the above as ‘side effects’ of our programme running. The name gives them a negative, medical, connotation - but really, they’re the meat of your app. Apps that don’t have side effects don’t talk to anything external, don’t worry about time, and don’t react to unexpected errors.
+
+## Actions
+
+> “Thank u, next.” - Ariana Grande
+
+Side effects can be expressed in two ways in XState. First, as a fire-and-forget `action`. Let’s imagine that when the user opens this modal, we want to report to a logging service that this happened.
+
+```ts
+const modalMachine = createMachine(
+ {
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: 'open',
+ },
+ },
+ open: {
+ entry: ['reportThatUserOpenedTheModal'],
+ on: {
+ CLOSE: 'closed',
+ },
+ },
+ },
+ },
+ {
+ actions: {
+ // Note that I'm declaring the action name above, but implementing
+ // it here. This keeps the logic and implementation separate,
+ // which I like
+ reportThatUserOpenedTheModal: async () => {
+ await fetch('/external-service/user-opened-modal');
+ },
+ },
+ },
+);
+```
+
+Actions are fire-and-forget, which means you can fire them off without worrying about consequences. If the action I declared above errors, my state machine won’t react - it’s already forgotten about it.
+
+Actions represent a single point in time, like a dot on a graph. This means that you can place them very flexibly. Above, I’ve placed them on the `entry` attribute of the state, meaning that action will be fired when we enter the state. I could also place them on the transition:
+
+```ts
+const modalMachine = createMachine(
+ {
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: {
+ actions: 'reportThatUserOpenedTheModal',
+ target: 'open',
+ },
+ },
+ },
+ open: {
+ on: {
+ CLOSE: 'closed',
+ },
+ },
+ },
+ },
+ {
+ actions: {
+ reportThatUserOpenedTheModal: async () => {
+ // implementation
+ },
+ },
+ },
+);
+```
+
+This means that whenever `OPEN` is called from the `closed` state, that action will be fired. In this modal, we could also fire the action whenever the user leaves the `closed` state, since we know they’ll be going to the `open` state.
+
+```ts
+const modalMachine = createMachine(
+ {
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: 'open',
+ },
+ exit: ['reportThatUserOpenedTheModal'],
+ },
+ open: {
+ on: {
+ CLOSE: 'closed',
+ },
+ },
+ },
+ },
+ {
+ actions: {
+ reportThatUserOpenedTheModal: async () => {
+ // implementation
+ },
+ },
+ },
+);
+```
+
+Actions are flexible precisely because we don’t care about their outcome. We can hang them on the hooks our state machine gives us: transitions between states, exiting states and entering states.
+
+## Services
+
+> “And I plan to be forgotten when I’m gone…” - The Tallest Man on Earth
+
+But actions have a specific limitation - they are designed to be forgotten. Let’s imagine that you wanted to track whether the analytics call you made was successful, and only open the modal if it was. We’ll add an `opening` state which handles that check.
+
+```ts
+const modalMachine = createMachine({
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: 'opening',
+ },
+ },
+ opening: {
+ entry: [
+ async () => {
+ await fetch('/external-service/user-opened-modal');
+
+ // OK, this was successful - how do I get to the open state?!
+ },
+ ],
+ },
+ open: {
+ on: {
+ CLOSE: 'closed',
+ },
+ },
+ },
+});
+```
+
+This isn’t possible in an action. We can’t fire back an event to the machine, because it’s already forgotten about us.
+
+When you care about the result of an action, put it in a service. This is how this would be expressed as a service:
+
+```ts
+const modalMachine = createMachine({
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: 'opening',
+ },
+ },
+ opening: {
+ invoke: {
+ // This uses the invoked callback syntax - my favourite
+ // syntax for expressing services
+ src: () => async (send) => {
+ await fetch('/external-service/user-opened-modal');
+
+ send('OPENED_SUCCESSFULLY');
+ },
+ onError: {
+ target: 'closed',
+ },
+ },
+ on: {
+ OPENED_SUCCESSFULLY: {
+ target: 'open',
+ },
+ },
+ },
+ open: {
+ on: {
+ CLOSE: 'closed',
+ },
+ },
+ },
+});
+```
+
+If actions are a dot on the graph, services represent a line - a continuous process which takes some amount of time. If the service errors, it’ll trigger an event called `error.platform.serviceName`, which you can listen for with the `onError` attribute, as above.
+
+Crucially, they can also send events back to the machine, using the `send` function above. Notice that we’re both sending back the `OPENED_SUCCESSFULLY` event _and_ listening to it in the `on: {}` attribute of the `opening` state.
+
+Services are less flexible than actions, because they demand more from you. You can’t hang them on every hook your machine offers. They must be contained within one state, and they’re cancelled when you leave that state. (Note: they can also be at the root of the machine definition, meaning they run for the lifetime of the machine.)
+
+## Guidelines
+
+Actions are the ‘thank u, next’ of the XState world. They represent points in time. Use them for fire-and-forget actions. Actions are great for:
+
+- `console.log`
+- Showing ephemeral error or success messages (toasts)
+- Navigating between pages
+- Firing off events to external services or parents of your machine
+
+Services are like a ‘phase’ your machine goes through. They represent a length of time. Use them for processes where you care about the outcome, or you want the process to run for a long time. Services are great for:
+
+- API calls
+- Event listeners (`window.addEventListener`)
diff --git a/fumadocs/content/blog/2021-05-13-why-i-love-invoked-callbacks/index.mdx b/fumadocs/content/blog/2021-05-13-why-i-love-invoked-callbacks/index.mdx
new file mode 100644
index 000000000..57e75ccdf
--- /dev/null
+++ b/fumadocs/content/blog/2021-05-13-why-i-love-invoked-callbacks/index.mdx
@@ -0,0 +1,186 @@
+---
+title: Why I love invoked callbacks
+description: >-
+ I’ve written a bit about services, but today I wanted to talk about my
+ favourite way of expressing services: the Invoked Callback.
+tags: [services, invoked callback, state machine, xstate]
+authors: [matt]
+image: /blog/2021-05-13-why-i-love-invoked-callbacks.png
+slug: 2021-05-13-why-i-love-invoked-callbacks
+date: 2021-05-13
+---
+
+XState offers several primitives for representing long-running application processes. These are usually expressed as [services](https://xstate.js.org/docs/guides/communication.html). I’ve written a bit about services [here](https://dev.to/mpocock1/xstate-should-this-be-an-action-or-a-service-2cp0) - but today I wanted to talk about my favourite way of expressing services: the Invoked Callback.
+
+{/* truncate */}
+
+The [Invoked Callback](https://xstate.js.org/docs/guides/communication.html#invoking-callbacks) combines immense flexibility with good readability and a solid Typescript experience. They look like this:
+
+```ts
+createMachine({
+ invoke: {
+ src: (context, event) => (send, onReceive) => {
+ // Run any code you like inside here
+
+ return () => {
+ // Any code inside here will be called when
+ // you leave this state, or the machine is stopped
+ };
+ },
+ },
+});
+```
+
+Let’s break this down. You get access to `context` and `event`, just like promise-based services. But `send` is where things really get interesting. Let’s break down what makes `send` useful with an example.
+
+## File Uploads
+
+Imagine you need to build a file uploader, and you have a handy function called `startUpload` that uploads some data, and exposes an `onProgressUpdate` parameter to update the progress.
+
+```ts
+createMachine({
+ context: {
+ progress: 0,
+ },
+ initial: 'idle',
+ states: {
+ idle: {
+ on: {
+ START: 'pending',
+ },
+ },
+ pending: {
+ on: {
+ PROGRESS_UPDATED: {
+ assign: assign({
+ progress: (context, event) => event.progress,
+ }),
+ },
+ CANCEL: {
+ target: 'idle',
+ },
+ },
+ invoke: {
+ src: (context) => (send) => {
+ const uploader = startUpload({
+ onProgressUpdate: (progress) => {
+ send({
+ type: 'PROGRESS_UPDATED',
+ progress,
+ });
+ },
+ });
+
+ return () => {
+ uploader.cancel();
+ };
+ },
+ },
+ },
+ },
+});
+```
+
+This machine starts in the `idle` state, but on the `START` event begins its invoked service, which uploads the file. It then listens for `PROGRESS_UPDATED` events, and updates the context based on its updates.
+
+The `CANCEL` event will trigger the `uploader.cancel()` function, which gets called when the state is left. React users may recognise this syntax - it’s the same as the cleanup function in the [useEffect hook](https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect).
+
+Note how simple and idiomatic it is to cancel the uploader - just exit the state, and the service gets cleaned up.
+
+## Event Listeners
+
+The invoked callback’s cleanup function makes it very useful for event listeners, for instance `window.addEventListener()`. XState Catalogue’s [Tab Focus Machine](https://xstate-catalogue.com/machines/tab-focus) is a perfect example of this - copied here for ease:
+
+```ts
+createMachine(
+ {
+ initial: 'userIsOnTab',
+ states: {
+ userIsOnTab: {
+ invoke: {
+ src: 'checkForDocumentBlur',
+ },
+ on: {
+ REPORT_TAB_BLUR: 'userIsNotOnTab',
+ },
+ },
+ userIsNotOnTab: {
+ invoke: {
+ src: 'checkForDocumentFocus',
+ },
+ on: {
+ REPORT_TAB_FOCUS: 'userIsOnTab',
+ },
+ },
+ },
+ },
+ {
+ services: {
+ checkForDocumentBlur: () => (send) => {
+ const listener = () => {
+ send('REPORT_TAB_BLUR');
+ };
+
+ window.addEventListener('blur', listener);
+
+ return () => {
+ window.removeEventListener('blur', listener);
+ };
+ },
+ checkForDocumentFocus: () => (send) => {
+ const listener = () => {
+ send('REPORT_TAB_FOCUS');
+ };
+
+ window.addEventListener('focus', listener);
+
+ return () => {
+ window.removeEventListener('focus', listener);
+ };
+ },
+ },
+ },
+);
+```
+
+When in the `userIsOnTab` state, we listen for the window’s `blur` event. When that happens, and `REPORT_TAB_BLUR` is fired, we clean up the event listener and head right on over to `userIsNotOnTab`, where we fire up the other service.
+
+## Websockets
+
+Invoked callbacks can also receive events via the `onReceive` function. This is perfect when you need to communicate to your service, such as sending events to websockets.
+
+```ts
+import { createMachine, forwardTo } from 'xstate';
+
+createMachine({
+ on: {
+ SEND: {
+ actions: forwardTo('websocket'),
+ },
+ },
+ invoke: {
+ id: 'websocket',
+ src: () => (send, onReceive) => {
+ const websocket = connectWebsocket();
+
+ onReceive((event) => {
+ if (event.type === 'SEND') {
+ websocket.send(event.message);
+ }
+ });
+
+ return () => {
+ websocket.disconnect();
+ };
+ },
+ },
+});
+```
+
+In order to receive events, services need an `id`. Not all events are forwarded to the invoked service, only those which we select via the `forwardTo` action.
+
+Here, we can connect to the websocket, establish two-way communication, and clean it up all in a few lines of code.
+
+## My Love Letter
+
+Invoked callbacks are a concise, flexible method of invoking services in XState. There isn’t a case they can’t cover - and they’re one of my favourite parts of the XState API.
diff --git a/fumadocs/content/blog/2021-05-27-global-state-xstate-react/index.mdx b/fumadocs/content/blog/2021-05-27-global-state-xstate-react/index.mdx
new file mode 100644
index 000000000..27d3120eb
--- /dev/null
+++ b/fumadocs/content/blog/2021-05-27-global-state-xstate-react/index.mdx
@@ -0,0 +1,131 @@
+---
+title: How to manage global state with XState and React
+description: 'Everything you need to know to manage global state with XState and React.'
+authors: [matt]
+date: 2021-05-27
+tags: [xstate, react, redux, webdev]
+category: entry
+image: /blog/2021-05-27-global-state-xstate-react.png
+slug: 2021-05-27-global-state-xstate-react
+---
+
+Many React applications follow the Flux architecture popularised by [Redux](https://redux.js.org/). This setup can be characterised by a few key ideas:
+
+1. It uses a single object at the top of your app which stores all application state, often called the **store**.
+2. It provides a single `dispatch` function which can be used to send messages up to the store. Redux calls these `action`s, but I'll be calling them `events` - as they're known in XState.
+3. How the store responds to these messages from the app are expressed in pure functions - most often in **reducers**.
+
+This article won't go into depth on whether the Flux architecture is a good idea. David Khourshid's article [Redux is half a pattern](https://dev.to/davidkpiano/redux-is-half-of-a-pattern-1-2-1hd7) goes into great detail here. For the purposes of this article, we're going to assume that you like having a global store, and you want to replicate it in XState.
+
+{/* truncate */}
+
+There are many reasons for wanting to do so. XState is second-to-none when it comes to managing complex asynchronous behaviour and modelling difficult problems. Managing this in Redux apps usually involves middleware: either [redux-thunk](https://github.com/reduxjs/redux-thunk), [redux-loop](https://github.com/redux-loop/redux-loop) or [redux-saga](https://github.com/redux-saga/redux-saga). Choosing XState gives you a first-class way to manage complexity.
+
+## A globally available store
+
+To mimic Redux's globally-available store, we're going to use React context. React context can be a tricky tool to work with - if you pass in values which change too often, in can result in re-renders all the way down the tree. That means we need to pass in values which change as little as possible.
+
+Luckily, XState gives us a first-class way to do that.
+
+```ts
+import React, { createContext } from 'react';
+import { useInterpret } from '@xstate/react';
+import { authMachine } from './authMachine';
+import { ActorRefFrom } from 'xstate';
+
+interface GlobalStateContextType {
+ authService: ActorRefFrom;
+}
+
+export const GlobalStateContext = createContext(
+ // Typed this way to avoid TS errors,
+ // looks odd I know
+ {} as GlobalStateContextType,
+);
+
+export const GlobalStateProvider = (props) => {
+ const authService = useInterpret(authMachine);
+
+ return (
+
+ {props.children}
+
+ );
+};
+```
+
+Using `useInterpret` returns a `service`, which is a static reference to the running machine which can be subscribed to. This value never changes, so we don't need to worry about wasted re-renders.
+
+## Utilising context
+
+Further down the tree, you can subscribe to the service like this:
+
+```ts
+import React, { useContext } from 'react';
+import { GlobalStateContext } from './globalState';
+import { useActor } from '@xstate/react';
+
+export const SomeComponent = (props) => {
+ const globalServices = useContext(GlobalStateContext);
+ const [state] = useActor(globalServices.authService);
+
+ return state.matches('loggedIn') ? 'Logged In' : 'Logged Out';
+};
+```
+
+The `useActor` hook listens for whenever the service changes, and updates the `state` value.
+
+## Improving Performance
+
+There's an issue with the implementation above - this will update the component for any change to the service. Redux offers tools for deriving state using selectors - functions which restrict which parts of the state can result in components re-rendering.
+
+Luckily, XState provides that too.
+
+```ts
+import React, { useContext } from 'react';
+import { GlobalStateContext } from './globalState';
+import { useSelector } from '@xstate/react';
+
+const selector = (state) => {
+ return state.matches('loggedIn');
+};
+
+export const SomeComponent = (props) => {
+ const globalServices = useContext(GlobalStateContext);
+ const isLoggedIn = useSelector(globalServices.authService, selector);
+
+ return isLoggedIn ? 'Logged In' : 'Logged Out';
+};
+```
+
+Now, this component will only re-render when `state.matches('loggedIn')` returns a different value. This is my recommended approach over `useActor` for when you want to optimise performance.
+Dispatching events
+
+For dispatching events to the global store, you can call a service's send function directly.
+
+```ts
+import React, { useContext } from 'react';
+import { GlobalStateContext } from './globalState';
+
+export const SomeComponent = (props) => {
+ const globalServices = useContext(GlobalStateContext);
+
+ return (
+ globalServices.authService.send('LOG_OUT')}>
+ Log Out
+
+ );
+};
+```
+
+Note that you don't need to call useActor for this, it's available right on the context.
+
+## Deviations from Flux
+
+Keen-eyed readers may spot that this implementation is slightly different from Flux. For instance - instead of a single global store, one might have several running machines at once: `authService`, `dataCacheService`, and `globalTimeoutService`. Each of them have their own `send` attributes, too - so you're not calling a global dispatch.
+
+These changes can be worked around. One could create a synthetic send inside the global store which called all the services' `send` function manually. But personally, I prefer knowing exactly which services my messages are being passed to, and it avoids having to keep events globally namespaced.
+
+## Summary
+
+XState can work beautifully as a global store for a React application. It keeps application logic co-located, treats side effects as first-class citizens, and offers good performance with `useSelector`. You should choose this approach if you're keen on the Flux architecture but feel your app's logic is getting out of hand.
diff --git a/fumadocs/content/blog/2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals/index.mdx b/fumadocs/content/blog/2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals/index.mdx
new file mode 100644
index 000000000..e59b273ca
--- /dev/null
+++ b/fumadocs/content/blog/2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals/index.mdx
@@ -0,0 +1,327 @@
+---
+title: 'useState vs useReducer vs XState - Part 1: Modals'
+description: >-
+ Managing state at different levels of complexity is hard. This series of
+ articles should help you make the right choices off the bat. Today we’re
+ starting with modals.
+tags: [modal, react, useState, useReducer, xstate]
+authors: [matt]
+image: /blog/2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals.png
+slug: 2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals
+date: 2021-07-28
+---
+
+Managing state at different levels of complexity is hard. Different tools make different trade-offs between readability, complexity and speed of development. The worst part is that as apps get more complex, it’s easy to regret choices that were made early on.
+
+This series of articles should help you make the right choice off the bat. The plan is to cover a bunch of state use cases, starting with the simple and graduating to more complexity as we go. We’ll see how easy they are to write, and also how they survive changing requirements.
+
+Today, we’re starting with [modals](https://material-ui.com/components/modal/).
+
+{/* truncate */}
+
+## useState
+
+For modals, the key piece of state is whether or not the modal is open. `useState` lets us capture that single piece of state pretty succinctly.
+
+```ts
+const [isOpen, setIsOpen] = useState(false);
+
+const open = () => {
+ setIsOpen(true);
+};
+
+const close = () => {
+ setIsOpen(false);
+};
+
+const toggle = () => {
+ setIsOpen(!isOpen);
+};
+```
+
+Highly readable, simple enough, fast to write, bug-proof. For a simple toggle like this, `useState` is great.
+
+## useReducer
+
+```ts
+const reducer = (state = { isOpen: false }, action) => {
+ switch (action.type) {
+ case 'OPEN':
+ return {
+ isOpen: true,
+ };
+ case 'CLOSE':
+ return {
+ isOpen: false,
+ };
+ case 'TOGGLE':
+ return {
+ isOpen: !state.isOpen,
+ };
+ default:
+ return state;
+ }
+};
+
+const [state, dispatch] = useReducer(reducer, { isOpen: false });
+
+const open = () => {
+ dispatch({ type: 'OPEN' });
+};
+
+const close = () => {
+ dispatch({ type: 'CLOSE' });
+};
+
+const toggle = () => {
+ dispatch({ type: 'TOGGLE' });
+};
+```
+
+`useReducer` gives us a reducer, a powerful centralized spot in our code where we can visualise the changes happening. However, it took us quite a few more lines of code to reach the same result as `useState`. For now, I’d say `useState` has the edge.
+
+## useMachine
+
+`useMachine` is a hook from XState, which allows us to use the power of state machines in our code. Let’s see how it looks.
+
+```ts
+const machine = Machine({
+ id: 'modalMachine',
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: {
+ target: 'open',
+ },
+ TOGGLE: 'open',
+ },
+ },
+ open: {
+ on: {
+ TOGGLE: 'closed',
+ CLOSE: 'closed',
+ },
+ },
+ },
+});
+
+const [state, send] = useMachine(machine);
+
+const open = () => {
+ send({ type: 'OPEN' });
+};
+
+const close = () => {
+ send({ type: 'CLOSE' });
+};
+
+const toggle = () => {
+ send({ type: 'TOGGLE' });
+};
+```
+
+_You can see the state machine on the [XState visualiser here](https://xstate.js.org/viz/?gist=cf1578f5baa04cf9408ef6e48695f04c)._
+
+It’s remarkably similar in structure to the reducer above. Similar amount of lines, nearly the same event handlers. The state machine takes the edge over the reducer because of being able to easily visualise its logic - that’s something the reducer can’t match.
+
+However, the `useState` implementation still has the edge for me. The simplicity of execution, the elegance. It’s hard to see how it could be beaten...
+
+## Alert: requirements changing
+
+Oh no. Requirements have changed. Now, instead of immediately closing, the modal needs to animate out. This means we need to insert a third state, `closing`, which we automatically leave after 500ms. Let’s see how our implementations hold up.
+
+### useState
+
+**Refactor 1**: Our initial `isOpen` boolean won't handle all the states we need it to any more. Let’s change it to an enum: `closed`, `closing` and `open`.
+
+**Refactor 2**: `isOpen` is no longer a descriptive variable name, so we need to rename it to `modalState` and `setModalState`.
+
+**Refactor 3**: `useState` doesn’t handle async changes by itself, so we need to bring in `useEffect` to run a timeout when the state is in the `closing` state. We also need to clear the timeout if the state is no longer `closing`.
+
+**Refactor 4**: We need to change the toggle event handler to add logic to ensure it only triggers on the `closed` and `open` states. Toggles work great for booleans, but become much harder to manage with enums.
+
+```ts
+// Refactor 1, 2
+const [modalState, setModalState] = useState('closed');
+
+// Refactor 3
+useEffect(() => {
+ if (modalState === 'closing') {
+ const timeout = setTimeout(() => {
+ setModalState('closed');
+ }, 500);
+ return () => {
+ clearTimeout(timeout);
+ };
+ }
+}, [modalState]);
+
+// Refactor 1, 2
+const open = () => {
+ setModalState('open');
+};
+
+// Refactor 1, 2
+const close = () => {
+ setModalState('closing');
+};
+
+// Refactor 1, 2, 4
+const toggle = () => {
+ if (modalState === 'closed') {
+ setModalState('open');
+ } else if (modalState === 'open') {
+ setModalState('closing');
+ }
+};
+```
+
+Yuck. That was an enormous amount of refactoring to do just to add a simple, single requirement. On code that might be subject to changing requirements, think twice before using `useState`.
+
+### useReducer
+
+**Refactor 1**: Same as above - we turn the `isOpen` boolean to the same enum.
+
+**Refactor 2**: Same as above, `isOpen` is now improperly named, so we need to change it to `status`. This is changed in fewer places than `useState`, but there are still some changes to make.
+
+**Refactor 3**: The same as above, we use `useEffect` to manage the timeout. An additional wrinkle is that we need a new action type in the reducer, `REPORT_ANIMATION_FINISHED`, to cover this.
+
+**Refactor 4**: The same as above, but instead of the logic being in the event handler, we can actually change the logic inside the reducer. This is a cleaner change, but is still similar in the amount of lines it produces.
+
+```ts
+// Refactor 1, 2
+const reducer = (state = { status: 'closed' }, action) => {
+ switch (action.type) {
+ // Refactor 2
+ case 'OPEN':
+ return {
+ status: 'open',
+ };
+ // Refactor 2
+ case 'CLOSE':
+ return {
+ status: 'closing',
+ };
+ // Refactor 3
+ case 'REPORT_ANIMATION_FINISHED':
+ return {
+ status: 'closed',
+ };
+ // Refactor 4
+ case 'TOGGLE':
+ switch (state.status) {
+ case 'closed':
+ return {
+ status: 'open',
+ };
+ case 'open':
+ return {
+ status: 'closing',
+ };
+ }
+ break;
+ default:
+ return state;
+ }
+};
+
+// Refactor 1
+const [state, dispatch] = useReducer(reducer, { status: 'closed' });
+
+// Refactor 3
+useEffect(() => {
+ if (state.status === 'closing') {
+ const timeout = setTimeout(() => {
+ dispatch({ type: 'REPORT_ANIMATION_FINISHED' });
+ }, 500);
+ return () => {
+ clearTimeout(timeout);
+ };
+ }
+}, [state.status]);
+
+const open = () => {
+ dispatch({ type: 'OPEN' });
+};
+
+const close = () => {
+ dispatch({ type: 'CLOSE' });
+};
+
+const toggle = () => {
+ dispatch({ type: 'TOGGLE' });
+};
+```
+
+This file required the same number of refactors as the `useState` implementation. One crucial advantage is that these refactors were mostly located together: most changes occurred inside the reducer, and the event handlers went largely untouched. For me, this gives `useReducer` the edge over `useState`.
+
+### useMachine
+
+**Refactor 1**: Add a new closing state, which after 500 milliseconds goes to the closed state.
+
+**Refactor 2**: Changed the targets of the `TOGGLE` and `CLOSE` actions to point at `closing` instead of `closed`.
+
+```ts
+export const machine = Machine({
+ id: 'modalMachine',
+ initial: 'closed',
+ states: {
+ closed: {
+ on: {
+ OPEN: {
+ target: 'open',
+ },
+ TOGGLE: 'open',
+ },
+ },
+ // Refactor 1
+ closing: {
+ after: {
+ 500: 'closed',
+ },
+ },
+ open: {
+ on: {
+ // Refactor 2
+ TOGGLE: 'closing',
+ CLOSE: 'closing',
+ },
+ },
+ },
+});
+
+const [state, send] = useMachine(machine);
+
+const open = () => {
+ send({ type: 'OPEN' });
+};
+
+const close = () => {
+ send({ type: 'CLOSE' });
+};
+
+const toggle = () => {
+ send({ type: 'TOGGLE' });
+};
+```
+
+> [See the changed machine here](https://xstate.js.org/viz/?gist=61bbac74d69894c9472571e44c98f765).
+
+The difference here is stark. A minimal number of refactors, all within the state machine itself. The amount of lines has hardly changed. None of the event handlers changed. AND we have a working visualisation of the new implementation.
+
+## Conclusion
+
+Before the requirements changed, `useState` was the champion. It’s faster, easier to implement, and fairly clear. `useReducer` and `useMachine` were too verbose, but `useMachine` took the edge by being easier to visualise.
+
+But after the requirements changed, `useState` hit the floor. It quickly became the _worst_ implementation. It was the hardest to refactor, and its refactors were in the most diverse places. `useReducer` was equally hard to refactor, with the same set of changes. `useMachine` emerged as the champion, with a minimal diff required to build in new, complex functionality.
+
+So if you’re looking to build a modal fast, use `useState`. If you want to build it right, use `useMachine`.
+
+I’m excited to work on this set of articles - I’m looking forward to tackling the toughest state models out there. What would you like to see covered in the next one? Some ideas:
+
+- Data fetching
+- Form state
+- Multi-step sequences (checkout flows, signup flows)
+
+Let me know in the comments below, and follow me for the next article!
diff --git a/fumadocs/content/blog/2021-10-02-intro-fsm-sc/index.mdx b/fumadocs/content/blog/2021-10-02-intro-fsm-sc/index.mdx
new file mode 100644
index 000000000..ef5efd7a5
--- /dev/null
+++ b/fumadocs/content/blog/2021-10-02-intro-fsm-sc/index.mdx
@@ -0,0 +1,182 @@
+---
+title: Introduction to state machines and statecharts
+description: Introduction to state machines and statecharts
+tags: [introduction, state machine, statechart, state]
+authors: [laura]
+category: entry
+image: /blog/2021-10-02-intro-fsm-sc.png
+slug: 2021-10-02-intro-fsm-sc
+date: 2021-10-02
+---
+
+
+
+
+
+Statecharts are a visual language used to describe the states in a process.
+
+You may have used similar diagrams in the past to design user flows, plan databases or map app architecture. Statecharts are another way of using boxes and arrows to represent flows, but with XState these flows are also executable code that can be used to control the logic in your applications.
+
+{/* truncate */}
+
+This guide covers the basics of statecharts in a beginner-friendly way, including:
+
+- [states](#states)
+- [transitions and events](#transitions-and-events)
+- [initial states](#initial-state)
+- [final states](#final-state)
+- [compound states](#compound-states)
+- [parallel states](#parallel-states)
+- [self-transitions](#self-transition)
+- [planning statecharts](#planning-statecharts)
+- [delayed transitions](#delayed-transitions)
+- [actions](#actions)
+
+## States
+
+The _states_ are represented by rounded rectangle boxes. To draw a statechart for the process of a dog, there are two states that would first come to mind:
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+A dog is always **asleep** or **awake**. The dog can’t be asleep and awake at the same time, and it’s impossible for the dog to be neither asleep nor awake. There’s only these two states, a precisely limited, _finite_ number of states.
+
+## Transitions and events
+
+How the dog goes between **asleep** and **awake** is through _transitions_, which are symbolised by an arrow pointing from one state to the next state in the process’s sequence.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+A transition is caused by an _event_ that results in the change of state. Transitions are labelled with their events.
+
+Transitions and events are _deterministic_. Deterministic means that each transition and event always points to the same next state, and always produces the same result from their given starting condition, every time the process is run. Dogs never **wake up** to become **asleep** or **fall asleep** to become **awake**.
+
+This tiny dog process, with its two finite states and two transitions is a _Finite State Machine._ A state machine is used to describe the behavior of something. The machine describes the thing’s states and the transitions between those states. It’s a Finite State Machine because it has a finite number of states. (Sometimes abbreviated to FSM by folks who love jargon).
+
+## Initial state
+
+Any process that has states will have an _initial state_, the default state the process exists in until an event happens to change the process’s state.
+
+The initial state is represented by a filled circle with an arrow pointing from the circle to the initial state.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+Using a statechart to describe the process of walking the dog, the initial state would be **waiting** to walk.
+
+## Final state
+
+Most processes with states will have a _final state_, the last state when the process is finished. The final state is represented by a double border on the state’s rounded rectangle box.
+
+In the dog walking statechart, the final state would be **walk complete**.
+
+
+
+## Compound states
+
+A compound state is a state that can contain more states, also known as child states. These child states can only happen when the parent compound state is happening. Inside the **on a walk** state, there could be the child states of **walking**, **running** and **stopping to sniff good smells**.
+
+A compound state is symbolised by a labelled rounded rectangle box that acts as a container for its child states.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+A compound state should also specify which child state is the initial state. In the **on a walk** compound state, the initial state is **walking**.
+
+Compound states are what makes statecharts capable of handling more complexity than an everyday state machine.
+
+### Atomic states
+
+An atomic state is a state that doesn’t have any child states. **Waiting**, **walk complete**, **walking**, **running** and **stopping to sniff good smells** are all atomic states.
+
+### Parallel states
+
+A parallel state is a compound state where all of its child states, also known as regions, are active simultaneously. The regions are separated inside the compound state container by a dashed line.
+
+Inside the **on a walk** compound state, there could be two regions. One region contains the dog’s activity child states of **walking**, **running** and **stopping to sniff good smells**, and the other region containing the dog’s tail states of **wagging** and **not wagging**. The dog can walk and wag its tail, run and wag its tail or stop and sniff while wagging its tail, it can also do any of these activities without wagging its tail.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+Both regions should also specify which child state is the initial state. In our **tail** region, the initial state is **not wagging**.
+
+### Self-transition
+
+A self-transition is when an event happens, but the transition returns to the same state. The transition arrow exits and re-enters the same state.
+
+A helpful way to describe a self-transition is “doing something, not going somewhere” in the process.
+
+In a **dog begging** process, there would be a **begging** state with a **gets treat** event. And for the dogs who love their food, no matter how many times you go through the **gets treat** event, the dog returns to its **begging** state.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+## Planning statecharts
+
+One of the benefits of statecharts is that, in the process of putting a statechart together, you explore all the possible states in your process. This exploration will help you avoid bugs and errors in your code as you’re more likely to cover all the eventualities.
+
+And because statecharts are executable, they can behave as both the diagram and the code, making it less likely that you’ll introduce differences or bugs interpreting between the diagramming and coding environments.
+
+### Planning a statechart for a login machine
+
+To draw a statechart for a login machine, start by listing the basic _events_ in the process. Think about what your login process will _do_:
+
+- log in
+- log out
+
+Then list the _states_ that exist as a result of those events:
+
+- logged in
+- logged out
+
+Once there’s some events and states, there’s the beginnings of a statechart.
+
+
+
+Don’t forget the _initial state_. In this case, the **logged out** state is the initial state, as any new user would come to the process logged out.
+
+## Delayed transitions
+
+Some login and logout processes will log out an inactive user after a fixed length of time as a security measure.
+
+The **active** and **idle** states only happen when the user is logged in, so these become child states inside the **logged in** compound state.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+The initial state inside the **logged in** compound state is **active**, as it happens as a direct result of the **log in** event, and logging in is a sign of user activity.
+
+A _delayed transition_ is a type of transition which happens after being in a state for a specified length of time. The delayed transition is labelled with “after” and a fixed duration to indicate how much time should pass before transitioning to the next indicated state.
+
+In the login statechart, a delayed transition of **60000** milliseconds, or 1 minute, follows the **active** state to determine whether the user is **idle**. If there is an **activity** event before the transition reaches one minute, the process returns to the **active** state.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
+
+A delayed transition of **180000** milliseconds, or 3 minutes, follows the **idle** state to transition to the **auto logged out** state if the user remains idle.
+
+## Actions
+
+A statechart is used to set off _actions_ in the system outside of the statechart. Actions are also commonly known as _effects_ or _side-effects_. “Side effects” sounds like a negative or unimportant term, but setting off actions is the primary purpose in using statecharts.
+
+Actions are events that have no impact or consequences for the rest of the sequence, the event is just triggered and the sequence moves on to the next step in the process. For example, the login statechart might execute actions that change the user interface.
+
+An _action_ can be fired upon entering or exiting a state, or on a transition. An action on a state is included inside the state’s container with an “entry /” or “exit /” label depending on whether the action should be fired on entry or exit from the state.
+
+In the login statechart, there’s an _entry_ action on the **idle** state to warn the user that they may be logged out.
+
+{/* no alt because the image is already described in the surrounding text */}
+
+
diff --git a/fumadocs/content/blog/2021-10-11-convince-teammates/index.mdx b/fumadocs/content/blog/2021-10-11-convince-teammates/index.mdx
new file mode 100644
index 000000000..7ba7f4532
--- /dev/null
+++ b/fumadocs/content/blog/2021-10-11-convince-teammates/index.mdx
@@ -0,0 +1,106 @@
+---
+title: How do you convince your teammates to use XState?
+description: Last week our question of the week was how do you convince your teammates to use XState? Here are some suggestions
+tags: [blog, team]
+authors: [laura]
+category: entry
+image: /blog/2021-10-11-convince-teammates.png
+slug: 2021-10-11-convince-teammates
+date: 2021-10-11
+---
+
+Last week our question of the week was “_how do you convince your teammates to use XState?_”
+
+One of our most frequent requests for the documentation is more advice on how to convince others to use XState. Many people [read an article](https://dev.to/mpocock1/how-to-manage-global-state-with-xstate-and-react-3if5), [watch a talk](https://www.youtube.com/watch?v=9k1ZHHJWt7k) or [participate in a workshop](https://frontendmasters.com/courses/xstate/) about XState and are sold on using XState themselves. But when it comes to getting their team on board, they often need more.
+
+{/* truncate */}
+
+Thanks to all the wonderful folks who answered our question [posted to Twitter](https://twitter.com/statelyai/status/1446035499777462274) and in our #office-hours [Discord](https://discord.gg/xstate) channel, we’ve got some fantastic suggestions.
+
+## Present using the Visualizer
+
+Many people immediately understand the benefits of statecharts as they’re familiar with diagrams using “boxes and arrows.” The Visualizer gives you an instant, recognisable overview of your application logic, which is often enough to get them on board.
+
+
+
+
+
+## Give examples using their own work
+
+Nick Hehr takes it step-by-step, utilizing the Visualizer.
+
+
+
+
+
+And Farzad successfully convinced his team. We discussed his success in [our office hours last week](https://www.youtube.com/watch?v=GCWZ-froHKo).
+
+## Give them side-by-side examples
+
+Amy Pellegrini recommended many complementary approaches, starting with comparing XState to other types of state management.
+
+
+
+This might be an intensive approach, but it’s the ultimate way to show the benefits of XState.
+
+## Show existing projects that are using XState
+
+We’re all more convinced by a technology when we know it’s already being used by projects or organisations whose work we admire. Amy Pellegrini suggests taking advantage of that:
+
+
+
+## Dictate the use as team lead
+
+A few people are fortunate enough to be leading their teams, and of course adoption is much easier when you can just tell your team to use XState from the top.
+
+[Cédric](https://discord.com/channels/795785288994652170/895591583586598943/895730595378913390):
+
+> I have said: "We will use XState" 😂
+
+
+
+## Become the expert so you can answer their questions and help them progress
+
+But not everyone can be the boss. Amy Pellegrini recommends becoming the XState subject matter expert, so you’re better prepared to support the rest of your team.
+
+
+
+And Josh Ferrell suggests collaborating with your product team, showing them how to model with the Visualizer, enabling them to do the modeling themselves in the future.
+
+
+
+## Tell them that the Stately team is fast and responsive
+
+Knowing a technology is under frequent development, and the developers are likely to respond to your feedback, is important to most of us when we’re embedding a new tool into a key part of our workflow. [Cédric on our Discord](https://discord.com/channels/795785288994652170/895591583586598943/895738949635551273) uses this as a way to convince his team to work with XState.
+
+> Xstate team improve the project really quickly, office hours give a good idea of the roadmap and features, when something is propose there are a real discussion and project improvement from only “an idea” on discord
+
+Thanks Cédric!
+
+## Promoting the collaboration benefits
+
+At Stately, we’re excited about the potential for collaboration with our Visualizer and Editor. We want to shrink the space between planning, design and development, helping the whole team work together on their application logic. [Cédric agrees with those benefits](https://discord.com/channels/795785288994652170/895591583586598943/895738949635551273).
+
+> The Viz is a real support to share business logic between product and tech team, this could be add[ed] into a specifications and of course with the futur[e] way to create machine by a simple drag and drop this offer[s] the possibility for the product team to create themselves a real machine useful [for] the tech team
+
+## Timing it right with a good use case for the team
+
+On our Discord, [Jan has a carefully planned approach](https://discord.com/channels/795785288994652170/895591583586598943/895754626689814578):
+
+> I received the task to build some pretty complicated UI components, and I opted to build them with simple state machines, implemented as good ol' `switch`-based reducers. Since we're using Redux, this approach didn't alienate my team entirely, and so the seed had been planted.
+>
+> Fast-forward a bit, and we're currently rewriting some of the core of our application as a state machine (or something of a Frankenstein statechart), after receiving new requirements would make it completely unmaintainable if we had continued in the same tracks. We're still doing it with `switch` (with nested states, even!), which isn't optimal, but it's way better than where we came from.
+>
+> For now it does the job, but my plan is to note any pains which could easily be solved with XState, and propose a rewrite if/when the time is right. I figure it will be a much easier sell if my team is already somewhat familiar with the concepts of state machines, and we have a really good use-case on our hands.
+
+Finding the right timing for your team is key for introducing any new technology.
+
+## Make them less intimidated by the terminology
+
+And sometimes a team is rightfully cautious of trying a technology that comes across as new. It’s all about how you frame it…
+
+
+
+😂
+
+In the future we hope to provide more documentation and advice to help you convince your team to use XState. If you have any suggestions or requests, please [let us know on our Discord](https://discord.gg/xstate). We want to make our tools work for you.
diff --git a/fumadocs/content/blog/2021-12-14-what-are-the-biggest-benefits-youve-had-from-using-state-machines/index.mdx b/fumadocs/content/blog/2021-12-14-what-are-the-biggest-benefits-youve-had-from-using-state-machines/index.mdx
new file mode 100644
index 000000000..fea80e964
--- /dev/null
+++ b/fumadocs/content/blog/2021-12-14-what-are-the-biggest-benefits-youve-had-from-using-state-machines/index.mdx
@@ -0,0 +1,84 @@
+---
+title: What are the biggest benefits of state machines?
+description: 'At Stately we’re fans of state machines, but we wanted to ask our
+ community… what are the benefits you’ve experienced from using state machines?'
+tags: [weekly question, community, state machine]
+authors: [laura]
+image: /blog/2021-12-14-what-are-the-biggest-benefits-youve-had-from-using-state-machines.png
+slug: 2021-12-14-what-are-the-biggest-benefits-youve-had-from-using-state-machines
+date: 2021-12-14
+---
+
+Last week we asked our community, “What are the biggest benefits you’ve had from using state machines?”
+
+At Stately, we’re obviously fans of state machines. Still, we wanted to ask our community the benefits they’ve experienced, whether they were working on a specific project or incorporating state machines into their everyday workflow.
+
+{/* truncate */}
+
+## Reusability
+
+Nowadays, so much of tech is component-oriented, so it was fascinating to hear from Martin that he reused the same state machines across different frameworks.
+
+
+
+> I think that **the biggest benefit comes from being able to use the same state machine across different frameworks**. With XState, even if you just use React on your projects, you can share your state machine with your [insert framework name here] friends.
+
+## Maintaining complex app logic
+
+
+
+Pat’s blog post goes into fascinating depth on his work with Crucible. The section on XState starts with Pat’s realization that he needed a state machine and ends with him moving much of their application logic into statecharts.
+
+> **We didn't start Crucible using XState but by the end of the project it was one of our most valuable tools…**
+>
+> …
+>
+> I was still new to statecharts and sort of overwhelmed by the whole idea so I needed to find a small feature I could prototype XState with where it would make sense and add recognizable value to a flow/process that could use more structure to it…
+>
+> …
+>
+> I built out the flow based on the services’ state and UI state, I defined all the valid transitions and where they’d go, I wired up the inputs/outputs to it, and then I turned it loose on matchmaking in an actual client and it worked beautifully.
+>
+> **I could look at the statechart and see the entire flow through the states**, I could see every valid transition that the statechart could take and where it would go, I could paste it into the XState visualizer and actually see a DIAGRAM OF ALL THE STATES AND WALK THROUGH THEM INTERACTIVELY.
+
+[Read all of Pat’s post on his blog](https://tivac.com/2020/10/19/crucible-rip/).
+
+## Maintainability
+
+Maintainability is a popular feature of state machines, and Joshua is a fan.
+
+
+
+Matt’s article on ‘[useState vs useReducer vs XState - Part 1: Modals](../../docs/assets/blog/2021-07-28-usestate-vs-usereducer-vs-xstate-part-1-modals/index)’ shares examples of these different approaches and their maintainability. And he followed it up with his [React Finland talk, ‘Make legacy code delightful with statecharts’](https://www.youtube.com/watch?v=zll9uDQOOq0).
+
+## Getting to the core of your logic
+
+The [discussion on our Discord](https://discord.com/channels/795785288994652170/795785288994652174/918546073268670534) focused on mindset and how state machines can help us better understand our app logic. Jan got the conversation started.
+
+> Certainly for me it must be that **it allows me to very quickly get to the essence and core behaviour of what I'm building**. Playing with boxes and arrows allow me to not get caught up in implementation details while figuring out how I want stuff to work.
+>
+> Afterwards, implementing actions and services just feels like filling in the blanks. Likewise hooking the machine up with my component is equally trivial: throw some `state.hasTag`, `state.context` and `send` in there, and you’ve got a stew going
+
+And Steve continued.
+
+> State machines make complex behaviors tractable; they help you think about the problem.
+>
+> State machines reveal complexities in ostensibly simple behaviors; they make you think about the problem.
+>
+> Yin and yang. Good cop, bad cop. **Thinking about your application behavior as a set of state machines focuses your mind on what's really going on inside that tangled mass of behaviors**.
+
+## Robustness
+
+Horacio also explained how the robustness of apps built with state machines could give us more faith in our work.
+
+> Programming is all about confidence, and for me, **working with state machines gives me the same confidence on my code as writing tests**, with the addition to have a visual representation of my system that I can discuss, show and modify with all my teammates.
+>
+> Also having the confidence of refactoring code without the fear of breaking other parts of my app!!
+>
+> Definitely XState is one of my top additions of 2021 to my dev toolbelt! 💪
+
+That’s just what we like to hear!
+
+Thank you, everyone, for your responses. We love to hear about how you’re using state machines in your projects. If you’ve got any feedback you want to share with the Stately team, you can [find us on Twitter](https://twitter.com/statelyai) and join the community on [our Discord server](https://discord.gg).
+
+_Note: quotes have been minimally edited to add emphasis and clarity._
diff --git a/fumadocs/content/blog/2022-01-27-introducing-typegen/index.mdx b/fumadocs/content/blog/2022-01-27-introducing-typegen/index.mdx
new file mode 100644
index 000000000..efe52c9fd
--- /dev/null
+++ b/fumadocs/content/blog/2022-01-27-introducing-typegen/index.mdx
@@ -0,0 +1,292 @@
+---
+title: 'Introducing: TypeScript typegen for XState'
+description: 'XState’s TypeScript experience just got an enormous upgrade - Typescript typegen, integrated with VS Code.'
+tags: [announcement, xstate, community, typescript, tutorial, vscode]
+authors: [matt]
+image: /blog/2022-01-27-introducing-typegen.png
+slug: 2022-01-27-introducing-typegen
+date: 2022-01-27
+---
+
+
+
+
+
+**XState and TypeScript are a match made in heaven**. TypeScript gives you type safety, and XState gives you logical safety. Together, they give you confidence that your code will do what you expect.
+
+However, we’ve been hearing from the community for some time that the _experience_ of using TypeScript with XState needed improving.
+
+Today's your lucky day. **XState’s TypeScript experience just got an _enormous_ upgrade**.
+
+{/* truncate */}
+
+We have brought type generation into XState v4.29.0, and we’re aiming for **perfect types on all parts of XState’s API**.
+
+We’re releasing typegen today as an opt-in beta. Be sure to check out the [known limitations](https://xstate.js.org/docs/guides/typescript.html#known-limitations) section of the docs.
+
+## Getting Started
+
+You can visit the [official docs](https://xstate.js.org/docs/guides/typescript.html#typegen) for the full guide, or follow the instructions:
+
+1. Download and install the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode).
+
+2. Open a new file and create a new machine, passing the schema attributes:
+
+```ts
+import { createMachine } from 'xstate';
+
+const machine = createMachine({
+ schema: {
+ context: {} as { value: string },
+ events: {} as { type: 'FOO'; value: string } | { type: 'BAR' },
+ },
+ initial: 'a',
+ states: {
+ a: {
+ on: {
+ FOO: {
+ actions: 'consoleLogValue',
+ target: 'b',
+ },
+ },
+ },
+ b: {
+ entry: 'consoleLogValueAgain',
+ },
+ },
+});
+```
+
+3. Add `tsTypes: {}` to the machine and save the file:
+
+```diff
+const machine = createMachine({
++ tsTypes: {},
+ schema: {
+ context: {} as { value: string },
+ events: {} as { type: "FOO"; value: string } | { type: "BAR" },
+ },
+ initial: "a",
+ states: {
+ /* ... */
+ },
+});
+```
+
+4. The extension should automatically add a generic to the machine:
+
+```ts
+const machine = createMachine({
+ // This generic just got added!
+ tsTypes: {} as import('./filename.typegen').Typegen0,
+ /* ... */
+});
+```
+
+5. Add a second parameter into the `createMachine` call - this is where you implement the actions, services, guards and delays for the machine.
+
+```ts
+const machine = createMachine(
+ {
+ /* ... */
+ },
+ {
+ actions: {
+ consoleLogValue: (context, event) => {
+ // Wow! event is typed to { type: 'FOO' }
+ console.log(event.value);
+ },
+ consoleLogValueAgain: (context, event) => {
+ // Wow! event is typed to { type: 'FOO' }
+ console.log(event.value);
+ },
+ },
+ },
+);
+```
+
+## Typing improvements
+
+Let’s get into the nitty-gritty and show you exactly what’s improved.
+
+### Events in machine options
+
+One of the most common pain points we heard from our community was using named actions, services and guards with TypeScript. The main reason is that you needed to write code like this:
+
+```ts
+createMachine(
+ {
+ schema: {
+ events: {} as { type: 'FOO'; value: string } | { type: 'BAR' },
+ },
+ on: {
+ FOO: {
+ actions: 'myAction',
+ },
+ },
+ },
+ {
+ actions: {
+ myAction: (context, event) => {
+ /**
+ * TS don't know if the event is FOO or BAR,
+ * so we have to defensively check here
+ */
+ if (event.type === 'FOO') {
+ /**
+ * Now we know that event.value
+ * is present on FOO, because
+ * we checked
+ */
+ console.log(event.value);
+ }
+ },
+ },
+ },
+);
+```
+
+The VS Code extension **statically analyzes your machine**, and knows which events lead to which actions.
+
+```ts
+createMachine(
+ {
+ /* config */
+ },
+ {
+ actions: {
+ myAction: (context, event) => {
+ /**
+ * No more defensive check needed! event
+ * is typed to only the events that cause
+ * the action
+ */
+ console.log(event.value);
+ },
+ },
+ },
+);
+```
+
+This works for actions, services, guards and delays. It even works for **entry actions**, another big pain point from the community.
+
+We’re hoping this lets you cut hundreds of lines of useless defensive code.
+
+### Autocomplete on machine options
+
+Another thing we’ve been hearing from the community is that it’s easy to make typos on machine options.
+
+Now, with typegen, you get **autocomplete on all machine options**. The following code will error:
+
+```ts
+createMachine(
+ {
+ entry: ['sayHello'],
+ },
+ {
+ actions: {
+ /*
+ * This will error, because sayhello does not
+ * exist in the machine declaration above
+ */
+ sayhello: () => {},
+ },
+ },
+);
+```
+
+We’ve also made it so any _missing_ machine options will error when
+you implement them later. So, in a React component:
+
+```tsx
+const machine = createMachine({
+ entry: ['sayHello'],
+});
+
+const App = () => {
+ /**
+ * This will error, because you haven't implemented
+ * sayHello in your actions object
+ */
+ const [state, send] = useMachine(machine);
+};
+```
+
+That gives you 100% confidence that your machine has all the things it needs to work.
+
+### Typing of promise-services
+
+Using promise-based services might be the single biggest pain point with XState and TypeScript. We used to have an entire troubleshooting section in our docs dedicated to them.
+
+Now, you can **strongly type the results of promise-based services**. Here’s how:
+
+```ts
+createMachine(
+ {
+ schema: {
+ /**
+ * Pass the 'services' attribute to schema,
+ * with all the names of your services and
+ * the data they return
+ */
+ services: {} as {
+ myService: {
+ // The data that gets returned from the service
+ data: { id: string };
+ };
+ },
+ },
+ invoke: {
+ src: 'myService',
+ onDone: {
+ actions: 'consoleLogId',
+ },
+ },
+ },
+ {
+ services: {
+ /**
+ * This service will now type error if it
+ * returns anything other than { id: string }
+ */
+ myService: async () => {
+ return {
+ id: '1',
+ };
+ },
+ },
+ actions: {
+ consoleLogId: (context, event) => {
+ /**
+ * This event now knows that it will
+ * receive a data attribute with { id: string }
+ */
+ console.log(event.data.id);
+ },
+ },
+ },
+);
+```
+
+This makes handling data return types in XState intuitive, easy and type-safe.
+
+## Acknowledgements
+
+I want to thank my Stately colleague [Andarist](https://twitter.com/AndaristRake). His TypeScript wizardry, incredible attention to detail and deep love of open source helped make this possible. We’ve literally been talking about this for 18 months - and it’s finally here.
diff --git a/fumadocs/content/blog/2022-02-14-modelling-101-how-to-build-a-statechart-from-scratch/index.mdx b/fumadocs/content/blog/2022-02-14-modelling-101-how-to-build-a-statechart-from-scratch/index.mdx
new file mode 100644
index 000000000..91213bdcd
--- /dev/null
+++ b/fumadocs/content/blog/2022-02-14-modelling-101-how-to-build-a-statechart-from-scratch/index.mdx
@@ -0,0 +1,196 @@
+---
+title: 'Modelling 101: How to build a statechart from scratch'
+description: Stately dev Matt Pocock takes you through a step-by-step guide on modelling statecharts
+tags: [xstate, tutorial, statechart, modelling]
+authors: [matt]
+image: /blog/2022-02-15-modelling-101-how-to-build-a-statechart-from-scratch.png
+slug: 2022-02-15-modelling-101-how-to-build-a-statechart-from-scratch
+date: 2022-02-17
+---
+
+Modelling using statecharts changed my career as a dev. Of all the state management solutions I’ve tried, it feels the most complete, logical and robust. Even if you don’t use them in your app’s code, statecharts let you break down complex features into states, events, services, actions and guards.
+
+{/* truncate */}
+
+It took me a long time to get comfortable modelling with statecharts. Even when I’d learned all the terms, it took time to work out a step-by-step process for building statecharts from scratch.
+
+Today, I’m going to share **an opinionated, step-by-step guide for building statecharts from scratch**. This process _works for me_, but it might not work for you. Feel free to tweak it as you go.
+
+## What you’ll need
+
+1. A pen and paper, or a digital notepad of some kind
+2. A statechart builder, such as [our visual editor](https://stately.ai/editor) or an XState machine in our [XState visualizer](https://stately.ai/viz)
+3. A clear idea of what you’re building. Maybe something you’ve implemented at work? You could also pick something from [XState Catalogue](https://xstate-catalogue.com).
+
+I’ve also built this [process as a statechart](https://stately.ai/registry/editor/d24479eb-ac20-44ae-a7b7-c0910e6247ad) using our visual editor.
+
+## 1. List all the possible events
+
+The first step in this process is to work out all the events that can be received by your statechart. You can think of an event as ‘something that happens’ in your app. There are plenty of examples even on this page:
+
+1. Press the escape key
+2. Press the space bar
+3. Select some text
+4. Click on an image
+
+You don’t need to list _all_ possible events that the user can perform. You only need to **list the events that your statechart cares about**. Here are some examples:
+
+For a submit form:
+
+- User changes the value of an input
+- User submits form
+
+For a spreadsheet:
+
+- User clicks a cell on the spreadsheet
+- User holds down the `shift`/`ctrl` key
+- User presses `escape`
+- User scrolls up or down
+
+Seeing all the events in a big list may start giving you an idea of what is possible in your statechart. You might start thinking in terms of _sequences of events_ — i.e. `User changes input` -> `User submits form`. Write down any sequences that pop into your head, they’ll be useful later.
+
+## 2. List all the possible tasks
+
+Next, it’s important to consider the _tasks_ your app needs to perform. These tasks could be called ‘side effects’ — things that happen as a result of your statechart running. These could be as diverse as:
+
+1. **Adding an item to a todo list (in local state)**
+2. **Sending a request to the API to load some data**
+3. **Focusing an input**
+4. **Waiting for a video to load**
+5. **Subscribing to something for updates** (perhaps via `window.addEventListener()`)
+
+_NOTE: I’m using ‘tasks’ loosely. This isn’t an official term in the XState docs — but ‘services’ and ‘actions’ are._
+
+Once you have a list of tasks, you need to divide them into two groups.
+
+### 2a. Services
+
+The first group is for services, tasks where you need to _do something when they finish_. I wrote a [longer guide about the distinction between actions and services here](../../docs/assets/blog/2021-04-30-should-this-be-an-action-or-a-service/index).
+
+From our list above, these are services:
+
+2. **Sending a request to the API to load some data**
+
+We need to get something from the API, meaning that we need to wait until we receive the data. This task can also fail — if we’re having network trouble or the API method fails. That means we care whether it succeeds or fails.
+
+4. **Waiting for a video to load**
+
+Same as above — we need to wait for the video to be loaded, and we care if it fails to load.
+
+5. **Subscribing to something for updates**
+
+Here, it’s a little different — when you subscribe to something, you need to clean up the listener to prevent a memory leak. For instance:
+
+```ts
+const listener = () => {
+ console.log('Hello!');
+};
+
+// Subscribe
+window.addEventListener('focus', listener);
+
+// Unsubscribe
+window.removeEventListener('focus', listener);
+```
+
+Here, we care about the outcome because we need to _run something at the end of the process_ — i.e. unsubscribe from the listener.
+
+#### Adding onDone/onError events
+
+Service completions/errors are handled _as events_ in your statechart, meaning they’re on the same level as your user clicking buttons.
+
+When you’ve got your list of services, note down two things:
+
+For each service that we need to wait for it to complete, add a `serviceName.onDone` event to your list.
+
+For each service that might reasonably be expected to error, add a `serviceName.onError` event to your list.
+
+### 2b. Actions
+
+The second group is for actions, tasks that you can ‘fire and forget’. Unlike services, the statechart forgets about actions as soon as they’re fired.
+
+From our list above, these are actions:
+
+1. **Adding an item to a todo list (in local state)**
+
+Changes to local state are pretty much always fire-and-forget. The reason is that, since we manage the local state ourselves, updating it is instant. [XState’s assign action](https://xstate.js.org/docs/guides/context.html#assign-action) is a good example.
+
+2. **Focusing an input**
+
+Focusing an input, in the same vein, is fire-and-forget. We don’t care about the outcome, and it’s unlikely to fail.
+
+## 3. Work out the very first state
+
+Now that you know _what can happen_ (events) and _what can be done_ (actions & services) in your statechart, it’s time to start adding some states.
+
+### 3a. Know your statechart’s lifecycle
+
+It’s always easiest to start at the beginning. Before you add your first state, consider the moment that your statechart gets initiated. What causes your statechart to run? Some examples:
+
+An **authentication** statechart, which manages the state for whether the user is logged in to a website or not. This would be started the _first moment_ the user clicks on to any page of your app, and finished when they close your app.
+
+A **sign up form** statechart, which handles a user signing up to your app. This might be started when the user visits the `/sign-up` route, and stopped when they exit it.
+
+### 3b. Write down your first state
+
+Now that you know what your app looks like when your statechart gets initiated, it’s time to name its initial state. Consider what the statechart is doing at that time. It could be `Loading data`, or `Waiting for user to submit form`, or even just `Idle`, waiting for something to happen.
+
+#### Dynamic initial states
+
+Every statechart _must_ have an initial state, and it can’t be dynamic — it must be the same every time your statechart runs.
+
+If you feel your statechart _does_ have more than one initial state (for instance it could start in two different modes) consider using a ‘checking’ state via an [eventless transition](https://xstate.js.org/docs/guides/transitions.html#eventless-always-transitions).
+
+## 4. Build out the states
+
+Now that you have your first state, you can start the process of building out the states. Every state represents a length of time, so consider _what is happening_ during that state.
+
+### 4a. Work out if any tasks are running
+
+Do you have any services running? If so, _invoke_ those services using [XState’s invoke property](https://xstate.js.org/docs/guides/communication.html#invoking-services).
+
+Does an action need to happen when you enter or exit the state? If so, add it as an [entry or exit action](https://xstate.js.org/docs/guides/actions.html#actions).
+
+Remember, the _statechart itself_ is also a state. We often call it the ‘root state’. This means that you can run services or listen to events for the _entire duration of your statechart_. You can also run entry actions when your statechart starts, and exit actions when it stops.
+
+### 4b. Work out which events can happen in that state
+
+Consider the period of time your state represents. Which events should _do_ something, and what should they do?
+
+#### Events that change state
+
+If an event results in:
+
+1. A new service running
+2. Something new appearing on screen
+3. Other types of events becoming possible
+4. A current service stopping
+
+Then it might need to move to a new state. A great example is a **data fetcher**. Your app is in two distinct states:
+
+- **Fetching data**: it doesn’t yet have the data, and the ‘fetch data’ service is running.
+- **Showing data**: it has the data, and is showing it on screen. The ‘fetch data’ service has stopped.
+
+If you have an event like this, draw out the new event and either create a new state, or make it target an existing one if needed.
+
+#### Events that don’t change state
+
+Sometimes, [events can be used to fire an action](https://xstate.js.org/docs/guides/actions.html#api) instead of changing state. A good example of this is when a form input changes, and you need to save the new value to local state.
+
+This is called a [self-transition](https://xstate.js.org/docs/guides/transitions.html#self-transitions), where the event doesn’t change the state — the state transitions to itself.
+
+#### Events that do nothing
+
+It’s important to bear in mind that when your statechart is in a certain state, _only the events that you specify_ will be handled. In other words, any event you don’t specify will do _nothing_ when it’s sent to the statechart.
+
+A classic example of this is a form. When you submit the form, you go to the ‘submitting’ state. It’s important that you don’t allow the ‘submit’ event to be received while in the ‘submitting’ state — otherwise the form might get sent twice!
+
+## 5. Keep going!
+
+Once you’ve figured out which actions/services are running in which states, and what all the events do, you’ve modelled your first state! You’ll likely have states which branch off your initial state — so go through those one-by-one and build them out.
+
+You can also leave parts of your statechart unimplemented, and dive into building the frontend/actions/services before returning to modelling again.
+
+I’ve found this approach really useful when getting to grips with what my app does. You can even use a statechart as an early validation tool to confirm that what you’re building is correct.
+
+If you’ve got any more questions, do [join our Discord](https://discord.gg/invite/xstate) and ask in the ‘modelling-help’ channel.
diff --git a/fumadocs/content/blog/2022-03-03-introducing-the-xstate-cli/index.mdx b/fumadocs/content/blog/2022-03-03-introducing-the-xstate-cli/index.mdx
new file mode 100644
index 000000000..5ba538680
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-03-introducing-the-xstate-cli/index.mdx
@@ -0,0 +1,57 @@
+---
+title: 'Introducing: The XState CLI'
+description: 'Get ready to run XState’s typegen commands outside of VSCode in our all-new CLI.'
+tags: [cli, xstate, announcement, typescript]
+authors: [matt]
+image: /blog/2022-03-03-introducing-the-xstate-cli.png
+slug: 2022-03-03-introducing-the-xstate-cli
+date: 2022-03-03
+---
+
+Around a month ago, we released [TypeScript Typegen](../../docs/assets/blog/2022-01-27-introducing-typegen/index) - an enormous upgrade to the TypeScript experience for XState.
+
+We’ve had a great response to it so far, but it’s only been available for VSCode users.
+
+Until now. With our new XState CLI, **you can get Typegen from the command line**.
+
+{/* truncate */}
+
+- Read the full documentation on the [`@xstate/cli` docs](https://xstate.js.org/docs/packages/xstate-cli).
+- Check out [our updated TypeScript Typegen guide](https://xstate.js.org/docs/guides/typescript.html#typegen).
+- Find the code on the [@xstate/cli GitHub repo](https://github.com/statelyai/xstate-tools/tree/main/apps/cli).
+
+## Installation
+
+```bash
+npm install @xstate/cli
+```
+
+OR
+
+```bash
+yarn add @xstate/cli
+```
+
+## Commands
+
+### `xstate typegen `
+
+```bash
+xstate typegen "src/**/*.tsx?"
+```
+
+Run the typegen against a glob of files. This will scan every targeted file and generate a typegen file accompanying it. It will also import the typegen into your file, as described in [our typegen documentation](https://xstate.js.org/docs/guides/typescript.html#typegen).
+
+Ensure you wrap your glob in quotes so that it executes correctly. Otherwise, you’ll get unexpected results.
+
+#### Options
+
+```bash
+xstate typegen "src/**/*.tsx?" --watch
+```
+
+Runs the task on a watch, monitoring for changed files and running the typegen script against them.
+
+## The Future
+
+We’re really excited about the CLI, and all the cool things it’ll enable. The typegen really is just the surface. If you’ve got ideas for what we could do with it, don’t hesitate to add a feature request to the [`xstate-tools` repo](https://github.com/statelyai/xstate-tools/issues).
diff --git a/fumadocs/content/blog/2022-03-11-xstate-vscode-extension-now-available-on-the-open-vsx-registry/index.mdx b/fumadocs/content/blog/2022-03-11-xstate-vscode-extension-now-available-on-the-open-vsx-registry/index.mdx
new file mode 100644
index 000000000..4d8541f7c
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-11-xstate-vscode-extension-now-available-on-the-open-vsx-registry/index.mdx
@@ -0,0 +1,35 @@
+---
+title: Open VSX XState VSCode extension now available
+description:
+ 'If you use VSCodium, Coder, Gitpod or another editor with VSCode-compatible
+ extensions, you can now install the XState VSCode extension.'
+tags: [extension, open source, xstate, vscode]
+authors: [laura]
+slug: /2022/03/11/xstate-vscode-extension-now-available-on-the-open-vsx-registry
+image: /blog/2022/03/11/xstate-vscode-extension-now-available-on-the-open-vsx-registry.png
+date: 2022-03-15
+---
+
+
+
+
+
+If you use VSCodium, Coder, Gitpod or another editor with VSCode-compatible extensions, you can now [install the XState VSCode extension from the Open VSX Registry](https://open-vsx.org/extension/statelyai/stately-vscode).
+
+{/* truncate */}
+
+We released our [XState VSCode extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) a few weeks ago, which gives you visual editing, autocomplete, typegen and linting for XState. The same extension is now available for VSCodium, Coder and Gitpod, and any other editors with VSCode-compatible extensions through the Open VSX Registry.
+
+We had many requests to distribute the extension on the Open VSX Registry. As a person who uses [VSCodium](https://vscodium.com) myself, it was exciting to install the extension from inside VSCodium.
+
+1. Open the command palette in VSCodium with `shift` + `cmd/ctrl` + `p`.
+2. Search for the **Install Extensions** command and hit enter to open the Extensions search.
+3. Search for **XState** to find the XState VSCode extension and install the extension using the **Install** button.
+4. Once you have installed the extension, you can find the extension commands by searching for **XState** in the command palette.
+
+You can also [download the XState VSCode extension directly from the Open VSX Registry](https://open-vsx.org/extension/statelyai/stately-vscode).
+
+Please give us your feedback and let us know if you encounter any issues in [our Discord](https://discord.gg/xstate) or [our xstate-tools repository](https://github.com/statelyai/xstate-tools/issues).
diff --git a/fumadocs/content/blog/2022-03-15-introducing-the-new-stately-homepage/index.mdx b/fumadocs/content/blog/2022-03-15-introducing-the-new-stately-homepage/index.mdx
new file mode 100644
index 000000000..18c9c76df
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-15-introducing-the-new-stately-homepage/index.mdx
@@ -0,0 +1,17 @@
+---
+title: Introducing the new Stately homepage
+description: 'Last week we launched the new Stately homepage, which we hope will make it easy to understand what Stately and XState are and help you convince your team to use state machines.'
+tags: [stately, homepage, introduction]
+authors: [laura]
+image: /blog/2022-03-16-introducing-the-new-stately-homepage.png
+slug: 2022-03-16-introducing-the-new-stately-homepage
+date: 2022-03-16
+---
+
+Last week we launched [the new Stately homepage](https://stately.ai), which we hope will make it easy to understand what Stately and XState are and help you convince your team to use state machines.
+
+You can watch us talk about the new design and its implementation during [last week’s office hours](https://youtu.be/WoPCd4D--Gk?t=771). I’m particularly impressed by David’s SVG arrows!
+
+{/* truncate */}
+
+And if you have any feedback about the homepage, please let us know on [our Discord](https://discord.gg/xstate). We always appreciate your thoughts.
diff --git a/fumadocs/content/blog/2022-03-23-stately-changelog-1/changelog-1-events.png b/fumadocs/content/blog/2022-03-23-stately-changelog-1/changelog-1-events.png
new file mode 100644
index 000000000..e7d697a5d
Binary files /dev/null and b/fumadocs/content/blog/2022-03-23-stately-changelog-1/changelog-1-events.png differ
diff --git a/fumadocs/content/blog/2022-03-23-stately-changelog-1/index.mdx b/fumadocs/content/blog/2022-03-23-stately-changelog-1/index.mdx
new file mode 100644
index 000000000..320bedf44
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-23-stately-changelog-1/index.mdx
@@ -0,0 +1,80 @@
+---
+title: 'Stately Changelog #1 - Snap To Elements'
+description: The Stately Editor changelog is where we discuss new features released to the Stately Editor beta.
+tags: [stately, announcement, editor, beta, changelog]
+authors: [matt]
+image: /blog/2022-03-23-stately-changelog-1.png
+slug: 2022-03-23-stately-changelog-1
+date: 2022-03-23
+---
+
+Happy Wednesday! Time for our first Editor Changelog blog, where we’ll talk about the new updates we’ve shipped in the editor.
+
+{/* truncate */}
+
+## Snap To Elements
+
+When you’re placing states and events in the canvas, you’ll now be able to snap them to surrounding elements.
+
+Snapping is a hugely requested feature and should help make your charts neater and more legible.
+
+So far, we’ve implemented snapping to the _center_ of elements only. We’re keen to [hear your feedback](https://github.com/statelyai/editor-feedback/issues) if you want more snapping!
+
+## Right-click menu
+
+You can now right-click events and states to perform actions including:
+
+- Renaming states/events
+- Making states the 'initial' state
+- Adding guards
+- Turning events into self-transitions
+
+And many more.
+
+## Visual update for events
+
+We’ve streamlined the way our events look to make them more visually distinct from states. This new look keeps the focus on the arrow, the direction of travel, and the flow of the chart.
+
+
+
+## Improvements to VSCode output
+
+We’ve massively improved the way we handle transition targets in VSCode. Instead of every transition having an ugly `#(machine).state1.state2`, the VSCode extension will now make the most idiomatic choice out of `targetState`, `.targetState`, or `targetState.child`.
+
+```ts
+const machine = createMachine({
+ initial: 'a',
+ states: {
+ a: {
+ on: {
+ EVENT: {
+ // Before
+ target: '#(machine).b',
+ },
+ },
+ },
+ b: {},
+ },
+});
+
+const machine = createMachine({
+ initial: 'a',
+ states: {
+ a: {
+ on: {
+ EVENT: {
+ // After
+ target: 'b',
+ },
+ },
+ },
+ b: {},
+ },
+});
+```
+
+As part of this work, we’ve also improved how we handle `internal` - you’ll now get proper warnings inside the visual editor when you use `internal` incorrectly.
+
+## Other Improvements
+
+We’ve been hard at work fixing bugs in the VSCode extension and the CLI. You can keep up to date with those changes in our [`xstate-tools`](https://github.com/statelyai/xstate-tools/releases) repo.
diff --git a/fumadocs/content/blog/2022-03-28-stately-is-hiring/index.mdx b/fumadocs/content/blog/2022-03-28-stately-is-hiring/index.mdx
new file mode 100644
index 000000000..fd3bd9ea2
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-28-stately-is-hiring/index.mdx
@@ -0,0 +1,59 @@
+---
+title: 'Stately is hiring!'
+description: We’re hiring for a frontend engineer, backend engineer, developer advocate and product designer at Stately.
+tags: [stately, hiring]
+authors: [laura]
+date: 2022-03-28
+image: /blog/2022/03/28/stately-is-hiring.png
+slug: /2022/03/28/stately-is-hiring
+---
+
+We’re hiring for a frontend engineer, backend engineer, developer advocate and product designer at Stately. You can [check out the Careers at Stately page on Notion](https://statelyai.notion.site/Careers-at-Stately-db3d704abd8f41058e5361e5ab9c1098).
+
+{/* truncate */}
+
+## About Stately
+
+Stately is building **the visual future of software development.**
+
+At Stately, we want to make app and business logic accessible to the entire team and eliminate barriers between development, design, and product to make it easier to build and maintain complex applications quickly and robustly.
+
+We enable development teams to collaborate effectively on even the most complex logic and flows with visual tools and insightful services. Helping them speak a common language so that the specifications, designs, and code are always synchronized, up-to-date, and visually clear.
+
+We’re building a team inspired to work on this mission to make software development better for everyone and practice what we preach, both inside Stately and everywhere else.
+
+## Questions from our office hours stream last week
+
+During [our office hours live stream last week](https://www.youtube.com/watch?v=zYCnVkaUT3w), we answered questions about working with Stately. You can find the questions and links to the timecode with our responses below.
+
+### About the job listings
+
+- [What are the responsibilities of the product designer role?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=826s)
+- [What is the hiring process?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=943s)
+- [What is the definition of a senior developer? (And other roles)](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1627s)
+- [Is there a trial period?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1175s)
+
+### About Stately
+
+- [When was the company founded, and how are you financially sustainable?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1115s)
+- [What exciting projects would you be working on at Stately?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=705s)
+
+### About the Stately team
+
+- [How many people are on the Stately team?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1225s)
+- [Should I apply if I feel underqualified? What would help me get the role in the future?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1313s)
+- [How well can Stately support neurodivergent people?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1969s)
+
+### About timezones and locations
+
+- [Are these jobs remote? Yes!](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=806s)
+- [Which timezones do Stately currently work within?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1753s)
+- [Where are you folks based?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1802s)
+
+### About the day-to-day at Stately
+
+- [What does team collaboration look like at Stately?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1364s)
+- [What is the work culture like from an insider perspective?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1845s)
+- [Are there a lot of video calls at Stately?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1441s)
+- [What is the balance of sync vs. async collaboration at Stately?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=1508s)
+- [What is a good day at Stately vs. a bad day at Stately?](https://www.youtube.com/watch?v=zYCnVkaUT3w&t=2029s)
diff --git a/fumadocs/content/blog/2022-03-29-introducing-the-new-stately-roadmap/index.mdx b/fumadocs/content/blog/2022-03-29-introducing-the-new-stately-roadmap/index.mdx
new file mode 100644
index 000000000..bf0536d9f
--- /dev/null
+++ b/fumadocs/content/blog/2022-03-29-introducing-the-new-stately-roadmap/index.mdx
@@ -0,0 +1,43 @@
+---
+title: Introducing the new Stately Roadmap
+description: 'We’ve added a Roadmap to our Documentation, so you know what we’re currently working on and what features are coming up soon.'
+tags: [stately, roadmap, introduction]
+authors: [laura]
+image: /blog/2022-03-29-introducing-the-new-stately-roadmap.png
+slug: 2022-03-29-introducing-the-new-stately-roadmap
+date: 2022-03-29
+---
+
+This week we’ve added [our Roadmap](https://xstate.js.org/docs/roadmap) to the XState documentation.
+
+Many of you have requested a roadmap to help you determine if it’s the right time to integrate XState and Stately tools into your team’s workflow. We’ve added a simple Roadmap so you know what we’re currently working on and what features are coming up soon.
+
+{/* truncate */}
+
+## Upcoming features
+
+To give you an idea of what we’re working on, below are the features currently planned for April - June 2022.
+
+### XState
+
+- XState v5 Alpha
+
+### Editor
+
+- Billing and Teams
+- Feature parity with XState
+- Authentication bug fixes
+- Welcome emails
+- Inline editing
+- Simulation mode
+- Markdown descriptions
+- Workflow creation
+
+### Documentation
+
+- Stately tools visibility
+- Update and rework
+
+## Please give us your feedback and suggestions
+
+We prioritize features based on your feedback. Please let us know if you have feedback or suggestions in [our GitHub Discussions](https://github.com/statelyai/xstate/discussions), [our Discord](https://discord.gg/xstate) or on [Twitter](https://twitter.com/statelyai).
diff --git a/fumadocs/content/blog/2022-04-05-building-a-video-player/index.mdx b/fumadocs/content/blog/2022-04-05-building-a-video-player/index.mdx
new file mode 100644
index 000000000..8cbdf46da
--- /dev/null
+++ b/fumadocs/content/blog/2022-04-05-building-a-video-player/index.mdx
@@ -0,0 +1,28 @@
+---
+title: Building a video player with XState and Stately tools
+description: 'A few weeks ago we uploaded a new video to the Stately YouTube channel showing how you can build basic video player functionality using XState and Stately tools.'
+tags: [xstate, modeling, tutorial]
+authors: [laura]
+date: 2022-04-05
+slug: /2022/04/05/building-a-video-player
+image: /blog//2022/04/05/building-a-video-player.png
+---
+
+A few weeks ago we uploaded a new video to the [Stately YouTube channel](http://youtube.com/c/statelyai) showing how you can build basic video player functionality using XState and Stately tools. You can watch the video below or [use the chapter links](#video-chapters) to jump to the chapter you want to watch.
+
+
+
+{/* truncate */}
+
+## Video chapters
+
+- [0:00](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=0s) What we want to build
+- [2:52](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=172s) Modeling the video player
+- [9:43](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=583s) Implementing the state machine
+- [19:49](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=1189s) Modeling with the visual editor
+- [22:10](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=1330s) Editing the state machine
+- [25:05](https://www.youtube.com/watch?v=3wZBSeLxVEw&t=1505s) Editing the state machine in the visual editor
+
+## We want your suggestions and feedback
+
+Please let us know what we should demo for you! Share your feedback or suggestions in [our GitHub Discussions](https://github.com/statelyai/xstate/discussions), [our Discord](https://discord.gg/xstate) or [Twitter](https://twitter.com/statelyai).
diff --git a/fumadocs/content/blog/2022-05-03-whats-new-may-2022/index.mdx b/fumadocs/content/blog/2022-05-03-whats-new-may-2022/index.mdx
new file mode 100644
index 000000000..1b083bc17
--- /dev/null
+++ b/fumadocs/content/blog/2022-05-03-whats-new-may-2022/index.mdx
@@ -0,0 +1,74 @@
+---
+title: 'What’s new in May 2022?'
+description: Updates to XState, @xstate/react and @xstate/fsm in the last month.
+tags: [stately, xstate, announcement, release]
+authors: [laura]
+image: /blog/2022-05-03-whats-new-may-2022.png
+slug: 2022-05-03-whats-new-may-2022
+date: 2022-05-03
+---
+
+What’s new to XState and Stately for May 2022?
+
+{/* truncate */}
+
+## Updates in XState
+
+With a few updates to @xstate/react and @xstate/fsm last month, our headline release was the `waitFor()` helper function.
+
+Read all the release notes and patch notes in the [XState GitHub repository releases](https://github.com/statelyai/xstate/releases).
+
+### `waitFor()`
+
+The new `waitFor(...)` helper function asynchronously waits for an actor’s emitted value to satisfy a predicate before a timeout.
+
+Example usage:
+
+```ts
+import { waitFor } from 'xstate/lib/waitFor';
+
+// ...
+const loginService = interpret(loginMachine).start();
+
+const loggedInState = await waitFor(loginService, (state) =>
+ state.hasTag('loggedIn'),
+);
+
+loggedInState.hasTag('loggedIn'); // true
+```
+
+### Updates in the @xstate/react package
+
+The @xstate/react package contains utilities for using XState with React. Find out more in the [@xstate/react package readme](https://github.com/statelyai/xstate/tree/main/packages/xstate-react).
+
+#### React 18
+
+The @xstate/react package now accepts React 18 as a peer dependency, and we rewrote the implementation to use the `use-sync-external-store` package. The package uses a shim to keep compatibility with older versions of React, so there is no need to worry if you haven’t upgraded yet.
+
+#### Subscribing to stopped interpreters
+
+Subscribing to a stopped interpreter will now always immediately emit the interpreter’s state and call a completion callback.
+
+### `asEffect` and `asLayoutEffect` removed
+
+We have removed the `asEffect` and `asLayoutEffect` action creators. These action creators didn’t fit the React model well and had the potential to cause issues as their existence suggested that they might be easy to use.
+
+If you want to execute actions at those exact times, you can either make a call directly from those effects or send events to the machine from those effects and execute explicit actions in response to said events.
+
+### `useMachine` and `useService` with @xstate/fsm
+
+We changed the signatures of `useMachine` and `useService` integrating with @xstate/fsm to match their signatures with their related hooks integrating with XState. Both now only accept a single generic. `useMachine` accepts `TMachine`, and `useService` accepts `TService`.
+
+### Updates in the @xstate/fsm package
+
+@xstate/fsm is a minimal, 1kb implementation of XState for finite state machines. Find out more in the [@xstate/fsm package readme](https://github.com/statelyai/xstate/tree/main/packages/xstate-fsm).
+
+#### `.start()`
+
+When you call .start() without any argument, it now always starts from the machine’s initial state, matching the behavior of XState itself.
+
+## More coming soon!
+
+If you want to know more about what we’ve got planned, you can [check out our Roadmap](https://xstate.js.org/docs/roadmap/). Got any feedback, or want to suggest features? [Share your thoughts in the XState discussions](https://github.com/statelyai/xstate/discussions).
+
+We’re also looking for your feedback on specific ideas in our [Stately RFCs (Requests for comment)](https://github.com/statelyai/rfcs).
diff --git a/fumadocs/content/blog/2022-06-07-whats-new-june-2022/dark-mode.png b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/dark-mode.png
new file mode 100644
index 000000000..c1303ecce
Binary files /dev/null and b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/dark-mode.png differ
diff --git a/fumadocs/content/blog/2022-06-07-whats-new-june-2022/index.mdx b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/index.mdx
new file mode 100644
index 000000000..cc35495d7
--- /dev/null
+++ b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/index.mdx
@@ -0,0 +1,43 @@
+---
+title: 'What’s new in June 2022?'
+description: Updates to XState, @xstate/test and the Stately editor in the last month.
+tags: [stately, xstate, announcement, release, lightmode, xstate test]
+authors: [laura]
+image: /blog/2022-06-07-whats-new-june-2022.png
+slug: 2022-06-07-whats-new-june-2022
+date: 2022-06-07
+---
+
+We’ve had a busy month and have plenty to share with you this June!
+
+{/* truncate */}
+
+## Another Stately update
+
+In May, we welcomed three new people to the Stately team. Kevin Maes and Anders Mellson join us as engineers, and Nick Perich as our product designer. Watch all three of them introduce themselves in [our office hours live stream from May 20](https://youtu.be/4WwaSZ-ciRI?t=127).
+
+## XState Test v1 alpha
+
+Last week we released an alpha version of the XState Test package.
+
+
+
+- Read more in the [model-based testing section in our alpha docs](https://graph-docs.vercel.app/model-based-testing/intro).
+- Find the [XState test tag release notes on GitHub](https://github.com/statelyai/xstate/releases/tag/%40xstate%2Ftest%401.0.0-alpha.0).
+- [Watch the video above on Twitter](https://twitter.com/statelyai/status/1531250605313892352).
+
+## Light mode in the Stately visual editor
+
+Nick and Anders got straight to work in their first week at Stately, creating light mode for the visual editor. We will keep improving both light mode and dark mode in the future, but we thought the initial designs were too good not to share with everyone!
+
+
+
+Toggle “Switch to light mode” in the editor’s menu to view the editor in light mode. Toggle “Switch to dark mode” to switch back to dark mode.
+
+
+
+## And there’s more to come!
+
+Want to know more about what we’ve got planned? [Check out our Roadmap](https://xstate.js.org/docs/roadmap/). Got any feedback or want to suggest features? [Share your thoughts in the XState discussions](https://github.com/statelyai/xstate/discussions).
+
+We’re also looking for your feedback on XState snippets, errors in XState, and more in our [Stately RFCs (Requests for comments)](https://github.com/statelyai/rfcs).
diff --git a/fumadocs/content/blog/2022-06-07-whats-new-june-2022/light-mode.png b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/light-mode.png
new file mode 100644
index 000000000..4ebd2266d
Binary files /dev/null and b/fumadocs/content/blog/2022-06-07-whats-new-june-2022/light-mode.png differ
diff --git a/fumadocs/content/blog/2022-06-13-nesting-typegen-files/index.mdx b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/index.mdx
new file mode 100644
index 000000000..ad1f056ff
--- /dev/null
+++ b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/index.mdx
@@ -0,0 +1,44 @@
+---
+title: 'Nesting XState typegen files'
+description: What is file nesting in VS Code and how to enable it for XState's generated type files.
+tags: [stately, extension, open source, xstate, vscode]
+authors: [anders]
+image: /blog/2022-06-13-nesting-typegen-files.png
+slug: nesting-typegen-files
+date: 2022-06-13
+---
+
+Our latest update to the [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) has made it easy to enable file nesting for typegen files. But what is file nesting?
+
+{/* truncate */}
+
+## Explorer file nesting
+
+[VS Code introduced file nesting](https://code.visualstudio.com/updates/v1_67#_explorer-file-nesting) in version 1.67 back in April of 2022. This feature is elegant as it allows you to group related files in the explorer view of VS Code.
+
+Our friend Erik Rasmussen quickly found this feature works well with the generated type files for XState. Thanks to Erik for his great idea!
+
+
+
+## Show me
+
+The image below shows the explorer in VS Code without file nesting enabled; you have a machine source file alongside a generated type file for that machine.
+
+
+
+After enabling file nesting for typegen files, VS Code explorer allows you to nest and collapse the generated file under the source file.
+
+
+
+### Enable nesting
+
+If you’re using our VS Code extension and haven’t manually enabled file nesting for typegen files, you will see a helper prompt allowing you to enable file nesting with a click.
+The prompt will show when you open a file with a machine.
+If you miss the prompt, you can enable or disable file nesting as described in the next section.
+
+### Disable nesting
+
+You can disable, or enable, the file nesting by using the `Xstate: Nest Typegen Files` toggle in your VS Code settings.
+
+
+PS: If you’re unsure what typegen is in XState, [read this post where we introduced TypeScript typegen for XState](../../docs/assets/blog/2022-01-27-introducing-typegen/index).
diff --git a/fumadocs/content/blog/2022-06-13-nesting-typegen-files/with-typegen.gif b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/with-typegen.gif
new file mode 100644
index 000000000..745bb0872
Binary files /dev/null and b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/with-typegen.gif differ
diff --git a/fumadocs/content/blog/2022-06-13-nesting-typegen-files/without-typegen.png b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/without-typegen.png
new file mode 100644
index 000000000..9d41386d0
Binary files /dev/null and b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/without-typegen.png differ
diff --git a/fumadocs/content/blog/2022-06-13-nesting-typegen-files/xstate-vscode-settings.png b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/xstate-vscode-settings.png
new file mode 100644
index 000000000..2c2e07241
Binary files /dev/null and b/fumadocs/content/blog/2022-06-13-nesting-typegen-files/xstate-vscode-settings.png differ
diff --git a/fumadocs/content/blog/2022-06-20-what-is-xstate-used-for/index.mdx b/fumadocs/content/blog/2022-06-20-what-is-xstate-used-for/index.mdx
new file mode 100644
index 000000000..8238d830f
--- /dev/null
+++ b/fumadocs/content/blog/2022-06-20-what-is-xstate-used-for/index.mdx
@@ -0,0 +1,83 @@
+---
+title: What is XState used for?
+description: XState can be used wherever JavaScript runs, whether on the backend or frontend. Because the code it creates can be visualized, it’s great at handling complex use cases.
+tags: [xstate, react, node, vue, redux]
+authors: [matt]
+image: /blog/2022-06-21-what-is-xstate-used-for.png
+slug: 2022-06-21-what-is-xstate-used-for
+date: 2022-06-21
+---
+
+XState can be used wherever JavaScript runs, whether on the backend or frontend. Because the code it creates can be visualized, it’s great at handling complex use cases - being able to see what a complex piece of code does can be extremely useful.
+
+Let’s look at each use case one-by-one.
+
+{/* truncate */}
+
+## On the web
+
+XState can be used in any frontend application to manage state. It has first-class integrations for [React](https://reactjs.org/), [Vue](https://vuejs.org/), [Svelte](https://svelte.dev/), as well as an upcoming [Solid](https://www.solidjs.com/) integration. It also works well in [Angular](https://angularjs.org/) without any integrations needed.
+
+You can try it in a Vanilla JS app by running this simple piece of code:
+
+```ts
+import { createMachine, interpret } from 'xstate';
+
+const machine = createMachine({
+ initial: 'waiting',
+ states: {
+ waiting: {
+ after: {
+ 2000: 'alerted',
+ },
+ },
+ alerted: {
+ entry: () => {
+ alert('Hello from the state machine!');
+ },
+ },
+ },
+});
+
+interpret(machine).start();
+```
+
+In this example, the machine will wait for 2 seconds, then call `alert()` to let you know it’s alive.
+
+There are plenty of ways XState can be used on the frontend - let’s talk about two of the most common patterns:
+
+### Complex components
+
+Most frontend apps split their code into components - individual pieces which can be reused across the app. If need to build a complex component, you can use XState to co-ordinate it.
+
+A great example of this is the library [Zag.js](https://zagjs.com/components/react/accordion), which is using XState-style syntax and statecharts to build reusable components across frameworks. You can even [see them visualized](https://state-machine-viz.vercel.app/accordion) using Stately’s tools.
+
+### Global state
+
+You can also use XState to manage global state in your apps. A common pattern is the [Flux](https://facebook.github.io/flux/docs/in-depth-overview/) architecture, where you dispatch events to a single top-level store. Parts of your app can subscribe to updates from that store, using [selectors](https://redux.js.org/usage/deriving-data-selectors).
+
+XState can **replace global state managers like [Redux](https://redux.js.org/) or [Vuex](https://vuex.vuejs.org/)**, with one major benefit - your global state can be visualised and [visually edited](https://stately.ai/editor). XState gives you the ability to create a global store, dispatch events to it, and subscribe to only the pieces that matter. See our specific instructions in [React](https://xstate.js.org/docs/recipes/react.html#global-state-react-context) for more info.
+
+## Native/extensions
+
+XState is also extremely useful in [React Native](https://reactnative.dev/) or [Electron](https://www.electronjs.org/) apps, Browser extensions and IDE extensions. Since XState doesn’t use any browser API’s, you can use it anywhere JavaScript runs.
+
+For instance, we use XState at Stately to [co-ordinate our VSCode extension](https://github.com/statelyai/xstate-tools/blob/6a91d6a5cdd4cf73c882175a2ad5fbe3b2a9910d/apps/extension/client/src/editorWebviewScript.ts#L90). [Centered](https://www.centered.app/) uses XState extensively in their Electron and React Native apps to co-ordinate timers, control app updates and handle complex user interactions.
+
+## Backend
+
+You can use XState in [Node.js](https://nodejs.org/en/) or [Deno](https://deno.land/) to build [serverless functions](https://vercel.com/docs/concepts/functions/serverless-functions) or persistent servers.
+
+For lambda functions, XState exposes a function called [`waitFor`](https://xstate.js.org/docs/guides/interpretation.html#waitfor), which allows you to wait for a state machine to be in a certain state. This allows you to use XState inside `async` functions with ease.
+
+To learn more about XState in the backend, see our [recent video](https://www.youtube.com/watch?v=qqyQGEjWSAw) introducing the topic.
+
+## Scripting and CLIs
+
+XState can be used when running scripts or CLIs to co-ordinate long-running processes.
+
+The most famous example of this in the wild is the frontend framework [Gatsby](https://www.gatsbyjs.com/). They use XState in their build process and development server to co-ordinate file-system changes and parallelize multiple processes. This means their entire build system is visualisable - an incredibly boon for such a complex process.
+
+## Summary
+
+XState works anywhere JS runs - and folks are using it in the wild for _all sorts_ of use cases. Any time you’re building something that feels remotely complex, you can use XState to simplify your code and enable powerful visual tooling.
diff --git a/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-canny.png b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-canny.png
new file mode 100644
index 000000000..5574b80ff
Binary files /dev/null and b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-canny.png differ
diff --git a/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-labeled-buttons.png b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-labeled-buttons.png
new file mode 100644
index 000000000..90a5c6d4a
Binary files /dev/null and b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-labeled-buttons.png differ
diff --git a/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-search.png b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-search.png
new file mode 100644
index 000000000..58a83fb20
Binary files /dev/null and b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/2022-07-06-search.png differ
diff --git a/fumadocs/content/blog/2022-07-06-whats-new-july-2022/index.mdx b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/index.mdx
new file mode 100644
index 000000000..9bf15256f
--- /dev/null
+++ b/fumadocs/content/blog/2022-07-06-whats-new-july-2022/index.mdx
@@ -0,0 +1,63 @@
+---
+title: 'What’s new in July 2022?'
+description: Search for machines, pinch to zoom, nest typegen files and more! Find out the latest features in the Stately editor and XState VS Code extension.
+tags: [stately, xstate, announcement, editor, search, typegen, survey, roadmap]
+authors: [laura]
+image: /blog/2022-07-06-whats-new-july-2022.png
+slug: 2022-07-06-whats-new-july-2022
+date: 2022-07-06
+---
+
+On top of our usual minor improvements and bug fixes, we’ve got great new features to share with you in July!
+
+{/* truncate */}
+
+## Search for machines
+
+Thanks to Anders’ hard work, the [Discover page](https://stately.ai/registry/discover) now has a fully-featured search where you can find and filter through all the machines our community has made.
+
+The filters can limit your search by source, features and number of states.
+
+
+
+We have more planned for search in the future that we can’t wait to share with you.
+
+## Pinch to zoom
+
+Trackpad fans rejoice; you can now pinch to zoom in and out to navigate the editor’s canvas by touch. This feature was a popular request at a workshop Matt gave, so he implemented it the following day! If you prefer a keyboard or mouse, you can also use the zoom in and out buttons or the **+** and **-** keyboard shortcuts.
+
+## Nesting typegen files
+
+You’re missing out if you’re not using [our XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) yet! After a top tip from our friend Erik Rasmussen, Anders added the feature to our VS Code extension. Read [Anders’ blog post on nesting XState typegen files](../../docs/assets/blog/nesting-typegen-files) for more details.
+
+## Labels on quick action buttons
+
+The quick action buttons for adding states and guarded transitions are now labeled with **+ State** and **+ Guard** to make the buttons’ function clearer. Improvements that make the editor’s interface easier to understand and use are a priority for us, and we appreciate any feedback you have for improvements that would benefit you.
+
+
+
+## More upcoming features
+
+We’ve got many more features coming up. Watch [our office hours from last week](https://www.youtube.com/watch?v=4v_M3HcZc3U) and [office hours from June 24](https://www.youtube.com/watch?v=qmbeYvcA3Ks) for demos of [smoother transition lines](https://www.youtube.com/watch?v=qmbeYvcA3Ks&t=530s), [fit to content on initial render](https://www.youtube.com/watch?v=4v_M3HcZc3U&t=1214s) and [parity between XState and the Stately editor](https://www.youtube.com/watch?v=4v_M3HcZc3U&t=208s).
+
+## Suggest features and help us prioritize our roadmap
+
+We’ve also started using Canny to collect your feedback and help us prioritize planned features in the last month. If there’s anything you want from XState or our Stately tools, please create a post or [upvote and give feedback on proposed features](https://feedback.stately.ai).
+
+Visit [our Canny roadmap](https://feedback.stately.ai) to make feature requests, submit feedback, view the roadmap and vote on features.
+
+
+
+## New videos on our YouTube channel
+
+Matt has made five great videos for [our YouTube channel](https://www.youtube.com/statelyai) to help you learn about using XState. Join our 1000+ subscribers and find out more about:
+
+- [How XState scales to ANY requirement](https://www.youtube.com/watch?v=AQHpXDo_S1k)
+- [XState in the backend](https://www.youtube.com/watch?v=qqyQGEjWSAw)
+- [Complex forms in XState & React](https://www.youtube.com/watch?v=Xa0H-vf2VuQ)
+- [Is XState good with TypeScript?](https://www.youtube.com/watch?v=EIi6CV4Bc_Q)
+- [How to decide between useState, useReducer and XState](https://www.youtube.com/watch?v=FrNXCJa5FLs)
+
+## Please give us feedback in our yearly survey
+
+And finally, [the Stately 2022 survey](https://stately.ai/survey) is out, and we would love you to add your response to help us make our tools work for you. All the questions are optional, and it will take just five minutes of your time.
diff --git a/fumadocs/content/blog/2022-07-18-just-use-hooks-xstate-in-react-components/index.mdx b/fumadocs/content/blog/2022-07-18-just-use-hooks-xstate-in-react-components/index.mdx
new file mode 100644
index 000000000..2f2ba5bf1
--- /dev/null
+++ b/fumadocs/content/blog/2022-07-18-just-use-hooks-xstate-in-react-components/index.mdx
@@ -0,0 +1,222 @@
+---
+title: '“Just Use Hooks”: XState in React Components'
+description: 'XState can be easily added to React components by using custom machine hooks for greater encapsulation and reusability.'
+tags: [component, hook, stately, xstate, react, state machine, prop]
+authors: [kevin]
+image: /blog/2022-07-18-just-use-hooks-xstate-in-react-components.png
+slug: 2022-07-18-just-use-hooks-xstate-in-react-components
+date: 2022-07-18
+---
+
+Are you a React developer using [XState](https://xstate.js.org/) to model your application logic? Perhaps you’ve heard of XState but have been looking for an easy way to try it out in one of your projects. If so, then I’d like to share with you a pattern I was introduced to when first diving into codebase at Stately, that of using **custom machine hooks**. This lightweight, reusable way to integrate XState into React components is a delight to work with and I think you might like it as much as I do!
+
+{/* truncate */}
+
+### Introduction
+
+In this post I’ll review the most common way to use the [`@xstate/react`](https://xstate.js.org/docs/packages/xstate-react/) library in a project. I’ll then demonstrate how encapsulation and reuse of state machines can be achieved by using hooks in your components, with some examples. I’ll also touch on the advantages and disadvantages to using this level of abstraction.
+
+For more background, you can check out [“Just Use Props”: An opinionated guide to React and XState](https://stately.ai/blog/2021-01-11-just-use-props-an-opinionated-guide-to-react-and-xstate) by Matt Pocock.
+
+After years of usage in the wild, and in response to confusion and frustration about hooks, the React Team has been putting a lot of effort into making the use of hooks clearer and simpler. Now is the perfect time to re-explore how hooks, when used effectively, can help make component creation easier.
+
+### XState in React components
+
+For those of you who are already using XState with React, you’re probably used to creating a machine using `createMachine()` and then passing that machine to the `useMachine` hook from within a component.
+
+Here is the code from the [Quick Start example](https://xstate.js.org/docs/packages/xstate-react/#quick-start) in the `@xstate/react` docs, where a `toggleMachine` ([view in the Editor](https://stately.ai/registry/editor/5e6286ca-5075-42da-9af8-99358530a0ac)) is created with `createMachine()` and then passed to `useMachine()` for use in a `Toggler` component.
+
+```tsx
+import { useMachine } from '@xstate/react';
+import { createMachine } from 'xstate';
+
+const toggleMachine = createMachine({
+ id: 'toggle',
+ initial: 'inactive',
+ states: {
+ inactive: {
+ on: { TOGGLE: 'active' },
+ },
+ active: {
+ on: { TOGGLE: 'inactive' },
+ },
+ },
+});
+
+export const Toggler = () => {
+ const [state, send] = useMachine(toggleMachine);
+
+ return (
+ send('TOGGLE')}>
+ {state.value === 'inactive'
+ ? 'Click to activate'
+ : 'Active! Click to deactivate'}
+
+ );
+};
+```
+
+This example shows how you can then evaluate `state.value` to render the corresponding UI for the toggle state and you can also call `send('TOGGLE')` in a button’s `onClick` handler to toggle the state.
+
+Similarly, one could access other state methods and properties like `state.matches()`, `state.can()`, `state.hasTag()`, or even `state.context` to evaluate state and show the correct UI.
+
+### A custom machine hook
+
+But what if your component really doesn’t need access to all of those features when using a machine? That’s where the custom machine hook comes into play. We can still create a machine and pass it to the `useMachine` hook but this can all be done inside of the custom hook.
+
+Here’s an example of what that custom hook might look like, including a [React/TypeScript CodeSandbox version](https://codesandbox.io/s/usetogglemachine-example-1-lazy-machine-8zcbvs?file=/src/Toggler.tsx):
+
+```ts
+import { useMachine } from '@xstate/react';
+import { useEffect } from 'react';
+import { createMachine } from 'xstate';
+
+export const useToggleMachine = (
+ initialActive: boolean = false,
+): [boolean, () => void] => {
+ const [state, send] = useMachine(() =>
+ createMachine({
+ id: 'toggle',
+ initial: initialActive ? 'active' : 'inactive',
+ states: {
+ inactive: {
+ on: { TOGGLE: 'active' },
+ },
+ active: {
+ on: { TOGGLE: 'inactive' },
+ },
+ },
+ }),
+ );
+
+ const isActive = state.matches('active');
+ const toggle = () => send('TOGGLE');
+
+ return [isActive, toggle];
+};
+```
+
+#### Why a hook?
+
+Why might we opt for this extra layer of abstraction? Well, consider what we need to do in the example:
+
+1. Create the `toggleMachine`, including states and possible transitions.
+2. Pass the machine to the `useMachine` hook.
+3. Expose the most relevant pieces to our component.
+
+From this hook, we can expose a minimal interface to components. In fact, we don’t even need to export the machine at all. By encapsulating the XState code, we allow components to focus on their core task, rendering UI as a function of data/props. Sweet!
+
+Our `useToggleMachine` hook now fully manages a toggle state for any component that uses it. This is now more resuable since a single component can create multiple instances of `useToggleMachine`. Similarly, multiple components can instantiate this hook one or more times to keep track of multiple, separate toggle states.
+
+Related: If you’re wondering about how to create a global machine hook then have a look at this [RFC for a Global Hooks API](https://github.com/statelyai/rfcs/pull/8).
+
+Let’s dive deeper into a the details of this `useToggleMachine` hook.
+
+#### Hook params
+
+If you recall, we initialized our machine to start out in its “inactive” state by specifying `initial: 'inactive'` in the machines config object. But we’re also receiving an `initialActive` value as the one and only argument passed into this hook. If that value is false or omitted, since it defaults to false, then the inital value will be in sync with the machine’s default state.
+
+But what if we want to start out with `initialActive` passed in as `true`? We need a way to immediately transiton our machine away from its own initial state to be synchronized with the incoming `initialActive` value.
+
+The original version of this post included an example that used the infamous `useEffect` hook to dynamically establish the initial state, based on the `initialActive` prop passed into the hook.
+
+```ts
+// This example is deprecated
+useEffect(() => {
+ if (initialActive && state.matches('inactive')) {
+ send('TOGGLE');
+ }
+}, [initialActive]);
+```
+
+Some readers noted in their feedback that they prefer to avoid using the `useEffect` hook altogether for understandable reasons. The `state` really should be included in the dependency array to make the linter happy and using `useEffect` here feels generally awkward.
+
+Instead, I've updated the example by wrapping the call to `createMachine` in a function passed to `useMachine`, utilizing a sort of ["lazily created machine"](https://xstate.js.org/docs/packages/xstate-react/#usemachine-machine-options).
+
+```ts
+const [state, send] = useMachine(() =>
+ createMachine({
+ id: 'toggle',
+ initial: initialActive ? 'active' : 'inactive',
+ states: {
+ inactive: {
+ on: { TOGGLE: 'active' },
+ },
+ active: {
+ on: { TOGGLE: 'inactive' },
+ },
+ },
+ }),
+);
+```
+
+This gives our machine config object access to the incoming `initialActive` prop so that we can dynamically assign the machine's initial value. It's a subtle but significant change.
+
+```ts
+initial: initialActive ? "active" : "inactive",
+```
+
+You can read about alternative methods and proposals in our [RFC for input](https://github.com/statelyai/rfcs/pull/9).
+
+#### Return values
+
+We’ve looked at the input param for `useToggleMachine` so now let’s look at its return values.
+
+```ts
+const isActive = state.matches('active');
+const toggle = () => send('TOGGLE');
+
+return [isActive, toggle];
+```
+
+We have a boolean `isActive` value which is derived from the state of the machine, the raison d’être of this hook. This is a simple mapping of one of two machine states to a boolean in this example. But you can imagine how states of a more complex machine might be derived from evaluating matches on the current state, possible next events, and even tags. Vist the [docs on state methods](https://xstate.js.org/docs/guides/states.html#state-methods-and-properties) for details.
+
+We also have a `toggle` function which enables us to toggle the state of the machine. It’s an anonymous function wrapping the call to XState’s `send('TOGGLE')`.
+
+Our hook returns an array of just these values much like `useState` or `useMachine` would and they should be destructured in the component.
+
+### Using the hook in a component
+
+What does this look like for the `Toggler` component to now use our `useToggleMachine` hook? It looks pretty good!
+
+```tsx
+const Toggler = () => {
+ const [isActive, toggle] = useToggleMachine(false); // Or pass true.
+
+ return Click me ({isActive ? '✅' : '❌'}) ;
+};
+```
+
+In that example, we use the value of `isActive` to specify the button’s text but it could easily be used for other purposes in this component or as a prop to pass down to child components.
+
+For the `Toggler` component’s `onChange` handler, we set its value to be the `toggle` function. Since that is already wrapping XState’s `send('TOGGLE')` call, we don’t even need to use another anonymous function. It all just works as is, in a tidy functional style.
+
+### Reusability
+
+As you can see, this pattern separates our normal React component code from our state machine implementation which keeps files neat and focused. Hooks make for more reusable machines across many components and in different situations. A `useToggleMachine` may be used to represent a toggle switch in one component but it might also represent the showing or hiding of UI or something else in another component.
+
+```ts
+const [isAnimationEnabled, toggleAnimation] = useToggleMachine(false);
+
+const [isDarkMode, toggleLightDarkMode] = useToggleMachine(true);
+```
+
+In a future blog post we can explore ways to compose machine hooks to build up more sophisticated machines from reusable parts, not unlike how small reusable functions are typically composed to create larger functions.
+
+### Team specialization
+
+This separation of code also means that **team members who are more familiar with XState can create and manage machine hooks** with autonomy. Meanwhile, their teammates, who may be less familiar with state machines or with XState, can still rapidly churn out UI components that will, nevertheless, be backed by the power of state machines. This greatly **facilitates incremental adoption**. You can begin using XState in small bits and pieces right away, neither needing to design your entire application as a large statechart nor rewrite everything to fit that way of working.
+
+### Caveats
+
+If you only have one component and all you need to do is toggle a boolean flag, then creating a machine and a hook on top of that may feel like unnecessary ceremony. Splitting code into two different files has the usual tradeoffs. Also, understanding how changing the `initialActive` prop works with the state machine’s own internal state can be a bit tricky although we’d still need to transition the machine to a non-initial state, in a similar way, from within a component that calls `useMachine()`.
+
+### Summary
+
+We saw a baseline example of how components traditionally employ the `useMachine` hook from `XState/react` with a [complete example](https://codesandbox.io/s/usetogglemachine-example-1-lazy-machine-8zcbvs?file=/src/Toggler.tsx) of how to separate the machine into its own custom `useToggleMachine` hook for comparison. We covered implementation details for this hook, as well as how to wire it up in a React component. I’ve offered several benefits that I believe make this abstraction worthwhile like incremental adoption and future feature scaling.
+
+### Next steps
+
+Again, the toggle example is a small yet usable example for creating a machine with XState and wrapping it in a hook. But we can take this even further. What about **combining multiple machines** into a single hook? How about **overriding machine implmentation details** via hooks on a per use basis? I’ll be exploring these patterns and more in some upcoming blog posts so stay tuned!
+
+In the meantime, if you like using XState then keep creating your own machines and try wrapping those in custom hooks to use in your components. Additonally, you can build upon machine/hook examples in these posts for your own purposes and even find machines in the [Discovery section of the Stately Studio](https://stately.ai/registry/discover) and turn those into hooks. Whatever path you take, I hope you **get hooked on using XState** to make your UI more robust and more reusable!
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/august-22-editor-improvements.png b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/august-22-editor-improvements.png
new file mode 100644
index 000000000..8640c7a74
Binary files /dev/null and b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/august-22-editor-improvements.png differ
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/index.mdx b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/index.mdx
new file mode 100644
index 000000000..5351f5710
--- /dev/null
+++ b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/index.mdx
@@ -0,0 +1,82 @@
+---
+title: 'What’s new in August 2022?'
+description: Get up and running faster with our XState VS Code extension and enjoy all the design details in the Stately studio experience.
+tags: [stately, xstate, announcement, vscode extension, studio, survey]
+authors: [laura]
+image: /blog/2022-08-03-whats-new-august-2022.png
+slug: 2022-08-03-whats-new-august-2022
+date: 2022-08-05
+---
+
+## Updates to our VS Code extension
+
+Our [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) has now been installed 10k times! Install the extension yourself from inside VS Code or find the [XState extension on the Open VSX Registry](https://stately.ai/blog/2022/03/11/xstate-vscode-extension-now-available-on-the-open-vsx-registry) to enjoy the following new features.
+
+{/* truncate */}
+
+### `xsm` snippet
+
+We want to make it fast and easy to use XState and the visual editor in your code. By adding the `xsm` snippet to our VS Code extension, we’ve made it possible to go from nothing to a full-blown machine you can open in the visual editor in a few keystrokes.
+
+Use our new `xsm` snippet ([Anders says “think **X** **S**tate **M**achine”](https://youtu.be/EkhWpJfVLDo?t=155)) with the latest version of the extension to insert a state machine template into your code, with the cursor ready for you to type the name of the machine.
+
+```js
+import { createMachine } from 'xstate';
+const nameOfMachine = createMachine({
+ id: 'nameOf',
+ initial: 'initialState',
+ states: {
+ initialState: {},
+ },
+});
+```
+
+Just give your machine a name, hit “Open visual editor,” and you’ll be able to continue creating your machine with the visual editor.
+
+
+
+### The editor now syncs with your VS Code theme
+
+You can now set your light mode or dark mode preference for the visual editor inside the VS Code extension:
+
+- Auto: syncs to your VS Code preference
+- Light: always uses light mode
+- Dark: always uses dark mode
+
+
+
+
+
+If the editor is open when you change the setting, you’ll need to close and re-open the editor tab to view the change.
+
+### Initial support for non-VS Code editors
+
+If you don’t use VS Code, we haven’t forgotten about you! We intend to support more code editors in the future and have recently taken the first step of making it easier to edit machine code using other code editors. Watch [Anders’ demo using Webstorm](https://www.youtube.com/watch?v=EkhWpJfVLDo&t=252s) during last week’s office hours to find out more.
+
+## Updates to Stately editor
+
+If you haven’t used the [Stately editor](https://stately.ai/editor) in a while, you’re in for a treat!
+
+### Canvas refinements
+
+We’ve made some small changes that have significantly changed how machines look and behave. Icons are now only used to distinguish the history states and final states, reducing the noise and increasing the readability of your machines.
+
+
+
+The transition line drawing algorithm has been improved further for straighter, more reasonable lines with fewer bends. The lines are now highlighted on hover, making them easier to select. We’ll continue to improve the algorithm so that it’s more intelligent about drawing even better lines.
+
+We’ve also removed the terminal squares at the end of the transition lines and put a little space between the transition lines and nodes. These subtle changes make the machines look less crowded, helping you focus more on your modeling.
+
+### Select all with the keyboard shortcut
+
+You can now use the cmd /ctrl **+** a keyboard shortcut to select all nodes and transitions in the editor. This shortcut is so common that you’ve probably tried to use it already… now the shortcut will work!
+
+
+
+## New privacy options
+
+We now have a modal asking for your consent the next time you log into the Stately Studio. We are sending some non-personal activity data to Mixpanel to help us understand how you use our features, but we need your permission to connect this data to a hash of your user ID. Many tech companies won’t ask for your explicit consent for similar activities. Still, at Stately, we want to use best practices regarding your privacy and be transparent about the minimal data we collect, how we use that data, and how we design for privacy by default. You can update your choices at any time in the studio settings.
+
+## Please give us feedback in our yearly survey
+
+Soon we’ll be closing [the Stately 2022 survey](https://stately.ai/survey). Do you know someone who isn’t using our tools but should be? Please get them to respond to our survey! We’d love to understand what features folks need to start using Stately tools. And if you’ve not responded yet, get your response in soon! All the questions are optional, and it will take just five minutes of your time.
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/select-all-keyboard-shortcut.gif b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/select-all-keyboard-shortcut.gif
new file mode 100644
index 000000000..5a25b2511
Binary files /dev/null and b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/select-all-keyboard-shortcut.gif differ
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-darkmode.png b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-darkmode.png
new file mode 100644
index 000000000..db2890c0b
Binary files /dev/null and b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-darkmode.png differ
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-lightmode.png b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-lightmode.png
new file mode 100644
index 000000000..3908a65ea
Binary files /dev/null and b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/vscode-lightmode.png differ
diff --git a/fumadocs/content/blog/2022-08-03-whats-new-august-2022/xsm-snippet.gif b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/xsm-snippet.gif
new file mode 100644
index 000000000..bb3439b46
Binary files /dev/null and b/fumadocs/content/blog/2022-08-03-whats-new-august-2022/xsm-snippet.gif differ
diff --git a/fumadocs/content/blog/2022-08-10-get-started-with-xstate-using-xsm-snippet/index.mdx b/fumadocs/content/blog/2022-08-10-get-started-with-xstate-using-xsm-snippet/index.mdx
new file mode 100644
index 000000000..250b2e2d5
--- /dev/null
+++ b/fumadocs/content/blog/2022-08-10-get-started-with-xstate-using-xsm-snippet/index.mdx
@@ -0,0 +1,13 @@
+---
+title: 'XState + VS Code: xsm snippet'
+description: Create a state machine in seconds and edit using our visual editor.
+tags: [stately, xstate, vscode extension, snippet]
+authors: [laura]
+image: /blog/2022-08-10-get-started-with-xstate-using-xsm-snippet.png
+slug: 2022-08-10-get-started-with-xstate-using-xsm-snippet
+date: 2022-08-10
+---
+
+Using the XState extension for VS Code, you can create a state machine in seconds and edit the machine using our visual editor. Use the `xsm` snippet to quickly generate the code required for your state machine, then drag and drop inside our visual editor to rapidly model your machine.
+
+
diff --git a/fumadocs/content/blog/2022-08-17-goodbye-use-effect-at-react-next/index.mdx b/fumadocs/content/blog/2022-08-17-goodbye-use-effect-at-react-next/index.mdx
new file mode 100644
index 000000000..7d75bbe5d
--- /dev/null
+++ b/fumadocs/content/blog/2022-08-17-goodbye-use-effect-at-react-next/index.mdx
@@ -0,0 +1,15 @@
+---
+title: '“Goodbye, useEffect” at React Next conference'
+description: Create a state machine in seconds and edit using our visual editor.
+tags: [stately, xstate, studio, conference, talk]
+authors: [david]
+image: /blog/2022-08-17-goodbye-use-effect-at-react-next.png
+slug: 2022-08-17-goodbye-use-effect-at-react-next
+date: 2022-08-17
+---
+
+From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much.
+
+Thankfully, there is a science (well, math) to side effects, formalized in state machines and statecharts, that can help us visually model and understand how to declaratively orchestrate effects, no matter how complex they get. In this talk, David ditches the `useEffect` hook and discovers how these computer science principles can be used to simplify effects in our React apps.
+
+
diff --git a/fumadocs/content/blog/2022-08-23-building-a-resizable-panel/index.mdx b/fumadocs/content/blog/2022-08-23-building-a-resizable-panel/index.mdx
new file mode 100644
index 000000000..86c57cd9e
--- /dev/null
+++ b/fumadocs/content/blog/2022-08-23-building-a-resizable-panel/index.mdx
@@ -0,0 +1,17 @@
+---
+title: 'Building a resizable panel'
+description: Farzad and David use XState to build the logic for a resizable panel with React in an impromptu live stream.
+tags: [stately, xstate, studio, react]
+authors: [farzad]
+image: /blog/2022-08-22-building-a-resizable-panel.png
+slug: 2022-08-22-building-a-resizable-panel
+date: 2022-08-22
+---
+
+Farzad and David use XState to build the logic for a resizable panel with React in an impromptu live stream.
+
+[Check out the accompanying code on CodeSandbox](https://codesandbox.io/s/resizable-panel-example-jjgkzl?file=/src/index.tsx).
+
+[Watch part 2](../../docs/assets/blog/2022-08-31-building-a-resizable-panel/index).
+
+
diff --git a/fumadocs/content/blog/2022-08-31-building-a-resizable-panel/index.mdx b/fumadocs/content/blog/2022-08-31-building-a-resizable-panel/index.mdx
new file mode 100644
index 000000000..024b962c8
--- /dev/null
+++ b/fumadocs/content/blog/2022-08-31-building-a-resizable-panel/index.mdx
@@ -0,0 +1,15 @@
+---
+title: 'Building a resizable panel: Part 2'
+description: Farzad and David add more features to their resizable panel using XState and React.
+tags: [stately, stately stream, xstate, studio, react]
+authors: [farzad]
+image: /blog/2022-08-31-building-a-resizable-panel.png
+slug: 2022-08-31-building-a-resizable-panel
+date: 2022-08-31
+---
+
+Farzad and David add more features to their resizable panel using XState and React. [Watch Part 1](../../docs/assets/blog/2022-08-23-building-a-resizable-panel/index).
+
+[Check out the accompanying code on Code Sandbox](https://codesandbox.io/s/resizable-panel-example-jjgkzl?file=/src/index.tsx).
+
+
diff --git a/fumadocs/content/blog/2022-09-14-whats-new-september-2022/human-readable-timers.png b/fumadocs/content/blog/2022-09-14-whats-new-september-2022/human-readable-timers.png
new file mode 100644
index 000000000..636e528b2
Binary files /dev/null and b/fumadocs/content/blog/2022-09-14-whats-new-september-2022/human-readable-timers.png differ
diff --git a/fumadocs/content/blog/2022-09-14-whats-new-september-2022/index.mdx b/fumadocs/content/blog/2022-09-14-whats-new-september-2022/index.mdx
new file mode 100644
index 000000000..2457e9002
--- /dev/null
+++ b/fumadocs/content/blog/2022-09-14-whats-new-september-2022/index.mdx
@@ -0,0 +1,41 @@
+---
+title: 'What’s new in September 2022?'
+description: 'The Stately team has got some huge features to share with you soon. Here’s what we’ve been working hard on through the summer!'
+tags: [stately, xstate, studio]
+authors: [laura]
+image: /blog/2022-09-14-whats-new-september-2022.png
+slug: 2022-09-14-whats-new-september-2022
+date: 2022-09-14
+---
+
+The Stately team has got some huge features to share with you soon. We’ve been working hard through the summer, which is why we’re already halfway into September by the time I’ve gotten around to this update post.
+
+{/* truncate */}
+
+## Human-readable timers
+
+When building after events, the convention is to specify the delay length in milliseconds, which is great for computers, but less easy for humans to understand. Now we display the timers in more human-friendly terms, including years, weeks, minutes, and seconds. You can still access the delay in milliseconds from the right panel when the event is selected.
+
+
+
+## New version alerts in the Stately studio
+
+We will now notify you in the browser when there is a new version of the studio. You’ll likely have seen other platforms with similar notifications in the past. This alert is our way of getting you the latest changes to the studio without the risk of losing your work in progress.
+
+[Watch the office hours from August 26 for more on the new version alerts](https://youtu.be/HI8LbL5Yu74?t=150).
+
+## XState 4.33.6 patch release
+
+We’re all looking forward to the XState version 5 release. In the meantime, we’ve got some migratory moves towards v5 in the form of the [XState 4.33.6 patch release](https://github.com/statelyai/xstate/releases/tag/xstate%404.33.6).
+
+Reading state directly from `someService.state` is now deprecated, and we encourage you to use `someService.getSnapshot()` instead. `.getSnapshot()` will become the go-to way to read the current snapshot from any actor.
+
+We’re also deprecating the `send()` action in favor of the new `sendTo()` action. `sendTo(actorName, event)` allows you to send to a specific, named actor with the `actorName` string. `sendTo()` paves the way for more improved typings and making the terms and concepts we use easier to understand for folks new to XState.
+
+[Find out more in David’s update on the 4.33.6 patch release from last week’s office hours](https://youtu.be/H0uTQbaW9rk?t=115).
+
+## Have you attended our office hours?
+
+If you want more regular updates from the Stately team, check out the [Stately office hours](https://youtube.com/playlist?list=PLvWgkXBB3dd6PXbSoctwDXjWX-AQrxhds). Every Friday at 10 AM ET, we live stream demos and discussions of our recent work at Stately and on XState. Live streaming on YouTube, Twitter, LinkedIn and Twitch, they’re a real highlight of our week, and we’re nearing our 50th edition 🎉.
+
+Be sure to join us at [our next office hours this Friday](https://www.youtube.com/watch?v=37YVzAjhDHA)!
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/index.mdx b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/index.mdx
new file mode 100644
index 000000000..88e39f4f2
--- /dev/null
+++ b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/index.mdx
@@ -0,0 +1,88 @@
+---
+title: Introducing Stately Studio 1.0
+description: We’re excited to announce the release of Stately Studio 1.0! We’ve been working on Stately Studio for over a year now, and we’re thrilled to share it with you.
+tags: [editor, studio, announcement]
+authors: [david]
+image: /blog/2022-10-18-introducing-stately-studio-1-0.png
+slug: 2022-10-18-introducing-stately-studio-1-0
+date: 2022-10-18
+---
+
+We’re excited to announce the release of Stately Studio 1.0! 🚀 Our mission is for all app logic to be visually collaborative and accessible to your entire team, and we’ve been working hard to make that a reality.
+
+{/* truncate */}
+
+## Thank you for your support!
+
+Earlier this year, we released [the first beta of Stately Editor & Stately Registry](../../docs/assets/blog/2022-2-6-stately-editor-public-beta/index) (together called the Stately Studio). We also released [Stately Viz](https://stately.ai/viz), a visualizer for XState machines, and the [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode). Since then, over 10,000 of you have tried our tools out, creating and sharing tens of thousands of machines (we don’t know the exact number because machines built with the extension are always private!)
+
+Thank you for giving our tools a go and for all the valuable feedback you’ve given us; we’ve been blown away by the response. We’re listening and continuing to improve the Stately Studio. As always, you can [create and upvote feature requests on our Canny roadmap](https://feedback.stately.ai).
+
+## Teams
+
+Collaborating on app logic is one of Stately’s key goals. That’s why we’ve introduced **teams**, where you can organize, privately share, and edit projects with your teammates. A **project** is a collection of related machines you can keep to yourself or share with your team. You can invite teammates to your team, and they can join your team by accepting the invitation.
+
+
+
+The teams feature is the first step to providing more powerful collaboration features. We’re working on GitHub integration with public and private repos, commenting, machine revisions with visual diffs, multiplayer mode for collaboratively editing machines in real time, and much more.
+
+Teams (previously known as Systems) are available on [our new Pro subscription](https://stately.ai/pricing).
+
+
+
+## Machine visibility and roles
+
+Machines may contain business logic you don’t want to share with the world; that’s why we’ve introduced **visibility** and **roles**. You can set a machine to be _private_, _public_, or _unlisted_. Private machines are only visible to you and your teammates. Public machines are visible to everyone, and [discoverable through search](https://stately.ai/registry/discover). Unlisted machines are only visible to those with the link, as they don’t appear in search results.
+
+
+
+You can also set roles for teammates to restrict how they can edit projects and machines. You can set a teammate to be a viewer, editor, or admin. Viewers can view projects and machines, but can’t edit them. Editors can edit projects and machines, but can’t delete them. Admins can do everything, including assigning roles.
+
+Private/unlisted machines and team roles are available on [our new Pro subscription.](https://stately.ai/pricing)
+
+## Editor redesign
+
+You might have noticed a refreshed redesign of the editor. The editor view is now more project-centric, allowing you to switch between machines with a single click. And we’ve improved the layout of states to give you more space to create and rearrange states.
+
+
+
+We’ve also redesigned guarded transitions. They are now individual transitions connected with a dotted line and a diamond, which should feel natural to anyone that has worked with flowcharts. The separated transitions make it easier to read and arrange the conditions for each transition, allowing you to change the source and target states of individual transitions quickly.
+
+
+
+Simulation mode has also been redesigned, making it easier to see active states and transitions. There are many more improvements to the editor, but we’ll leave them to you to discover.
+
+## More export options
+
+You asked, and we listened (we’re currently in the listening state). We’ve added more export options beyond JSON, including JavaScript and TypeScript. We have plans to add more export options in the future, including:
+
+- Markdown
+- CodeSandbox
+- StackBlitz
+- SVG, PNG, PDF
+- And others
+
+[Request your favorite export options on our Canny roadmap](https://feedback.stately.ai).
+
+
+
+## What’s next?
+
+We have so many things planned for the future:
+
+- A Stately GitHub integration for public and private repos
+- XState parity: supporting all of [XState’s features](https://github.com/statelyai/xstate) in the Stately Studio (we’re almost there!)
+- More export options and an embeddable editor
+- Support for other graph-based tools, CI/CD pipelines and other workflow engines/DSLs
+- Static graph analysis to help you catch logic errors ahead of time, which is difficult to do with plain code
+- Many more examples, tutorials, videos, and documentation
+
+## Try out the Studio
+
+All our new Pro features are available with a free trial you can try with your whole team. Upgrade today using the Upgrade button in [the Stately Studio](https://stately.ai/editor) or Sign in with GitHub to create a new account.
+
+
+
+If you’re a Product Hunt fan, you can also [find Stately Studio 1.0 on Product Hunt](https://www.producthunt.com/posts/stately-studio-1-0).
+
+Have any questions? Join us in [our office hours live stream this Friday](https://www.youtube.com/watch?v=JcT7iJOPFjE), where we’ll answer your questions and give you more demos.
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-editor.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-editor.png
new file mode 100644
index 000000000..0b1fcb708
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-editor.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-export.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-export.png
new file mode 100644
index 000000000..c9c355bfe
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-export.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-guards.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-guards.png
new file mode 100644
index 000000000..4f8074187
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-guards.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-projects.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-projects.png
new file mode 100644
index 000000000..36d34d8ef
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-projects.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-simulate.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-simulate.png
new file mode 100644
index 000000000..566c94454
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-simulate.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-teams.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-teams.png
new file mode 100644
index 000000000..1474bb3dc
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-teams.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-upgrade.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-upgrade.png
new file mode 100644
index 000000000..d4ac37665
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-upgrade.png differ
diff --git a/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-visibility.png b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-visibility.png
new file mode 100644
index 000000000..88badb40d
Binary files /dev/null and b/fumadocs/content/blog/2022-10-18-stately-studio-1-0/studio-1-0-visibility.png differ
diff --git a/fumadocs/content/blog/2022-10-27-studio-tutorials/index.mdx b/fumadocs/content/blog/2022-10-27-studio-tutorials/index.mdx
new file mode 100644
index 000000000..b61b9b932
--- /dev/null
+++ b/fumadocs/content/blog/2022-10-27-studio-tutorials/index.mdx
@@ -0,0 +1,67 @@
+---
+title: Learn the Stately Studio with our new video tutorials
+description: If you’re new to the Stately Studio, state machines or statecharts, we have the videos for you! Our new Stately Studio tutorials playlist on YouTube features bite-size videos to help you get started with understanding statecharts and state machines, and start modeling in the Stately Studio.
+tags: [editor, studio, tutorials, video]
+authors: [laura]
+image: /blog/2022-10-27-studio-tutorials.png
+slug: 2022-10-27-studio-tutorials
+date: 2022-10-27
+---
+
+If you’re new to the Stately Studio, state machines or statecharts, we have the videos for you! Our new Stately Studio tutorials playlist on YouTube features bite-size videos to help you get started with understanding statecharts and state machines, and start modeling in the Stately Studio.
+
+{/* truncate */}
+
+Each video explains a concept with an example machine and shows you how to use the features in the Stately Studio. You can [watch the whole playlist](https://www.youtube.com/playlist?list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ) in one go or watch the videos individually, as shown below.
+
+## What are state machines and statecharts?
+
+
+
+## States
+
+
+
+## Initial states
+
+
+
+## Transitions and events
+
+
+
+## Parent and child states
+
+
+
+## Parallel states
+
+
+
+## Final states
+
+
+
+## Entry and exit actions
+
+
+
+## Delayed (after) transitions
+
+
+
+## State done events
+
+
+
+## Invoked actors
+
+
+
+## Descriptions
+
+
+
+## More videos coming soon
+
+We’ll be adding more videos to the playlist very soon! Do you want us to make tutorials for a concept you need help understanding? Or how to use a particular feature in the Stately Studio? [Leave us a request on our Canny roadmap](https://feedback.stately.ai).
diff --git a/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-highlighted-selected-event.png b/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-highlighted-selected-event.png
new file mode 100644
index 000000000..72bc8dacf
Binary files /dev/null and b/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-highlighted-selected-event.png differ
diff --git a/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-login-providers.png b/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-login-providers.png
new file mode 100644
index 000000000..57fcfebb2
Binary files /dev/null and b/fumadocs/content/blog/2022-11-22-studio-update/2022-11-22-studio-update-login-providers.png differ
diff --git a/fumadocs/content/blog/2022-11-22-studio-update/index.mdx b/fumadocs/content/blog/2022-11-22-studio-update/index.mdx
new file mode 100644
index 000000000..94f5ebc24
--- /dev/null
+++ b/fumadocs/content/blog/2022-11-22-studio-update/index.mdx
@@ -0,0 +1,47 @@
+---
+title: 'New: Safari support, new login providers, and more!'
+description: It’s been just over a month since we released Stately Studio 1.0, and the team has barely slowed down! We’ve got more features live in the Studio today and even more planned before the end of the year.
+tags: [editor, studio, safari, login, performance]
+authors: [laura]
+image: /blog/2022-11-22-studio-update.png
+slug: 2022-11-22-studio-update
+date: 2022-11-22
+---
+
+It’s been just over a month since [we released Stately Studio 1.0](../../docs/assets/blog/2022-10-18-stately-studio-1-0/index), and the team has barely slowed down! We’ve got more features live in the Studio today and even more planned before the end of the year.
+
+[Watch a demo of these features in our release video](https://www.youtube.com/watch?v=N9QtPcknqDw).
+
+{/* truncate */}
+
+## Safari support
+
+We’re happy to tell you [the Stately Studio](https://stately.ai/studio) now works in Safari. It took a significant code refactor, which doesn’t just mean that you can now edit your statecharts using Safari; the new code structure should also give us bonuses for accessibility and future integrations. If you’re curious about what David refactored, you can watch him talk about the Safari support during [our office hours from last week](https://youtu.be/JBckQTgeD9g?t=2153).
+
+Safari support is still in beta, you’ll get a message warning you when you open the Studio in Safari for the first time. Your machines are not in danger; you might encounter a few bugs when using the Safari browser. If you do come across any bugs, please let us know in the #studio-feedback channel in [our Discord](https://discord.gg/xstate), and we’ll try to get them fixed as quickly as possible.
+
+## Sign up and log in with Twitter, Google, and email
+
+You can now log into the Stately Studio with Twitter or Google, or set up an account with your email address and password. We’ve had GitHub login support since we first launched the Studio, and alternative login providers have been a popular request from our community.
+
+
+
+Are there any login providers we should add? Please let us know as a [feature request on our Studio board](https://feedback.stately.ai/studio). Many options are available to us, and we aim to support the community’s most popular providers.
+
+## More editor improvements
+
+We’ve continued making usability improvements to the editor. You might notice that transition lines now run underneath the nodes, resulting in tidier, more readable machines by default. We also now highlight the selected event and its target and source states, fading out other nodes. Changes like this should help you focus and understand the machine, especially inside vast and complex machines.
+
+
+
+## Better overall performance
+
+The team has also migrated the Studio to tRPC v10 on the back end and TanStack Query on the front end. The migration isn’t a change you can see, but it has enormously improved the performance of the Studio, making everything feel speedier and smoother.
+
+## Want to know what’s coming up?
+
+If you want to keep up with the team’s latest work-in-progress, [subscribe to our YouTube channel](https://youtube.com/statelyai). We share our work, upcoming features, and answer your questions in our regular office hours live streams. [Check out last week’s office hours live stream](https://www.youtube.com/watch?v=JBckQTgeD9g) for a peek into features we’re looking to release very soon.
+
+## Request the features you want
+
+If you’ve watched [our office hours](https://www.youtube.com/watch?v=JBckQTgeD9g&list=PLvWgkXBB3dd6PXbSoctwDXjWX-AQrxhds), you know the team listens to every feature request and loves to make our community happy. You can request features, upvote other people’s suggestions, and check out what’s in progress on [our Canny roadmap](https://feedback.stately.ai).
diff --git a/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-create-machine.png b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-create-machine.png
new file mode 100644
index 000000000..ec798c8f5
Binary files /dev/null and b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-create-machine.png differ
diff --git a/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-importer.png b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-importer.png
new file mode 100644
index 000000000..efd0e7f4f
Binary files /dev/null and b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-importer.png differ
diff --git a/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-left-drawer.png b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-left-drawer.png
new file mode 100644
index 000000000..85d96ca53
Binary files /dev/null and b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-left-drawer.png differ
diff --git a/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-right-panel.png b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-right-panel.png
new file mode 100644
index 000000000..715f8393a
Binary files /dev/null and b/fumadocs/content/blog/2022-11-29-import-from-code/2022-11-29-import-from-code-right-panel.png differ
diff --git a/fumadocs/content/blog/2022-11-29-import-from-code/index.mdx b/fumadocs/content/blog/2022-11-29-import-from-code/index.mdx
new file mode 100644
index 000000000..5252d3021
--- /dev/null
+++ b/fumadocs/content/blog/2022-11-29-import-from-code/index.mdx
@@ -0,0 +1,43 @@
+---
+title: 'New in the Studio: Import from code'
+description: One of our most-requested features has just landed in the Stately Studio; you can now import from code!
+tags: [editor, studio, import, code, xstate]
+authors: [laura]
+image: /blog/2022-11-29-import-from-code.png
+slug: 2022-11-29-import-from-code
+date: 2022-11-29
+---
+
+One of our most-requested features has just landed in the Stately Studio; you can now import your machines from code!
+
+[Watch import from code in action in our latest video](https://www.youtube.com/watch?v=DAoIFaugDLo).
+
+Importing from code is handy if you’ve already built machines while working with [XState](https://xstate.js.org/docs), or have created a machine using our older [Stately Viz](https://stately.ai/viz) and haven’t yet tried the [Stately visual editor](https://stately.ai/editor).
+
+{/* truncate */}
+
+### How to import from code
+
+You can import an existing machine from JavaScript and TypeScript using the code panel in the right tool menu or from the **Machines** list in the left drawer.
+
+
+
+
+
+Your code should be formatted as a [`createMachine()` factory function](https://xstate.js.org/docs/guides/machines.html) before import. The importer has basic validation in case your machine has basic errors, including reminding you if the `createMachine` definition is missing.
+
+
+
+And please import your code into an existing machine with caution! Importing code from the code panel will overwrite the current machine in the editor. You may have multiple `createMachine`s included in the code you insert in the text area, but the Studio will currently only import the first machine found in the code. We plan to support importing multiple machines in the future, if that’s a feature you’d love, please [upvote the multiple machine import feature request](https://feedback.stately.ai/editor/p/import-multiple-machines-from-one-code-import) on our board.
+
+
+
+## Export code
+
+What about exporting your machines as code? You might have missed this feature as we released it as part of the [Stately Studio 1.0 release](https://stately.ai/blog/2022-10-18-introducing-stately-studio-1-0#more-export-options). You can export your machine as JSON, JavaScript, and TypeScript using the code panel in the right tool menu.
+
+Using the export icon button in the editor’s top bar, you can also export as code from the **Export machine** dialog. [Check out Laura’s demo utilizing this feature to go from modeling your machine to code](https://www.youtube.com/watch?v=NVQai9SBpiU).
+
+## Please give us your feedback!
+
+Have you used the import and export features yet? We’d love to hear your thoughts! The team loves hearing your feedback [on our Stately Discord](https://discord.gg/xstate) or during our next [office hours live stream](https://youtube.com/statelyai). If you have any feature requests, please add them to our [feature request board](https://feedback.stately.ai), and we’ll keep you updated if it’s planned, in progress, or complete!
diff --git a/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/index.mdx b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/index.mdx
new file mode 100644
index 000000000..b69119c40
--- /dev/null
+++ b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/index.mdx
@@ -0,0 +1,43 @@
+---
+title: 'New in the Studio: Machine restore'
+description: >-
+ We have introduced a new feature to the Stately Studio, a feature we hope
+ you’ll never see. Let’s call it machine restore, for lack of a better description.
+tags: [recovery, restore, offline, editor, studio]
+authors: [anders]
+image: /blog/2022-12-22-machine-recovery.png
+slug: 2022-12-22-machine-recovery
+date: 2022-12-13
+---
+
+We have introduced a new feature to the Stately Studio, a feature we hope you’ll never see. Let’s call it machine restore, for lack of a better description.
+
+{/* truncate */}
+
+### What's the challenge?
+
+At this moment in time, the Studio is using a traditional server-client architecture. Whenever you edit a machine, we will persist these changes to our central database, ready for you to use when you return - on any device with an internet connection.
+
+
+
+But what happens with your edits if your internet connection fails or the Stately backend is having problems? This is where our new feature comes into play.
+
+### Example
+
+In our example, Santa’s delivery machine has a problem; the Christmas-time state is unreachable. So we do as any good Santa’s helper would; we create an event to get to Christmas. But alas, we’re offline, so our changes could get lost. Before this new feature, there was a chance you wouldn’t notice you’d gone offline or that the server returned an error. But as you can see in the GIF below, we’ve introduced new error banners to show you what’s happening.
+
+
+
+### How to restore
+
+We try our best to detect any failure or missing connectivity when you edit your machines. If you hit any error state, we will inform you and start saving your work locally on your device. So next time you visit the Studio from that same device, we will compare what we saved with the machine from the server. And if they are different, we will give you the option to restore the safe copy to a new machine.
+
+You can see this in action using our example from above. When I return to the Studio, I can restore the changes I did while offline. Christmas is saved.
+
+
+
+Hopefully, you’ll never see this feature, but if you do, I hope you find it helpful.
+
+### What's next
+
+We have some exciting plans around an offline-first approach for the Studio which should make this restore feature obsolete and also enable features such as multi-player editing. Stay tuned for more news about this in 2023.
diff --git a/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_offline_error.gif b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_offline_error.gif
new file mode 100644
index 000000000..e6a39e690
Binary files /dev/null and b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_offline_error.gif differ
diff --git a/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_restore_machine.gif b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_restore_machine.gif
new file mode 100644
index 000000000..5483a2668
Binary files /dev/null and b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/recovery_restore_machine.gif differ
diff --git a/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/studio_architecture_challenges.png b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/studio_architecture_challenges.png
new file mode 100644
index 000000000..028260b20
Binary files /dev/null and b/fumadocs/content/blog/2022-12-13-new-in-the-studio-machine-disaster-recovery/studio_architecture_challenges.png differ
diff --git a/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/index.mdx b/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/index.mdx
new file mode 100644
index 000000000..80a546407
--- /dev/null
+++ b/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/index.mdx
@@ -0,0 +1,78 @@
+---
+title: Stately Editor public beta
+description: We’re excited to announce the public beta of the Stately Editor! The Stately Editor is a tool for creating and editing state diagrams. We’ve received a lot of great feedback from the private beta testers, and now we’re delighted to share it with everyone.
+tags: [editor, announcement, beta]
+authors: [david]
+image: /blog/2022-02-08-join-the-stately-editor-public-beta.png
+slug: 2022-02-08-join-the-stately-editor-public-beta
+date: 2022-02-08
+---
+
+We’re excited to announce the public beta of the Stately Editor! The Stately Editor is a tool for creating and editing state diagrams. We’ve received a lot of great feedback from the private beta testers, and now we’re delighted to share it with everyone.
+
+**[Try the Stately Editor beta now](https://stately.ai/editor?source=blog)**
+
+{/* truncate */}
+
+
+
+The software development lifecycle today is a bit fragmented. It’s very difficult for user stories, designs, code, tests, and documentation to stay in sync, leading to parts going out-of-date or even missing entirely. Bugs increase, and adding or modifying features becomes increasingly complex. And even understanding all the features and use-cases of an app becomes a daunting task.
+
+Our mission is to **make app logic accessible to the entire team**, including developers, designers, and stakeholders. Using state machines and statecharts as a common visual language, the software development cycle becomes more collaborative, eliminating handoff and tightening the feedback loop.
+
+The Stately Editor aims to be that collaborative visual app logic tool. Before, developers could build state machines and statecharts in code using [XState](https://github.com/statelyai/xstate). Now, you can create those same state diagrams visually.
+
+And yes, it goes both ways: edit the code and see the updated state diagram. Edit the state diagram and see the updated code in real-time. All this is possible with the [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode).
+
+## Features
+
+The editor supports everything you need to build state machines and statecharts:
+
+- Atomic, compound, parallel, history, and final states
+- Normal, guarded, and delayed events/transitions
+- Entry, exit, and transition actions
+- Invocations
+- Done and error transitions for states and invocations
+- State and event descriptions
+
+There are currently two modes:
+
+- **Editor mode**: add, modify, and delete states, events, and transitions, as well as data like actions, descriptions, and invocations.
+- **Simulation mode**: run the statechart, trigger events, and see the active state nodes.
+
+You can also **export machines to JSON**, ready to be used in your codebase with XState.
+
+## Roadmap
+
+We have a long list of features planned for the editor, including:
+
+- Parameterized actions, guards, and invocations
+- Built-in actions, such as `assign` and `raise`
+- Schema editing for `context` and `events`
+- More granular simulation control, such as specifying event payload and simulating delays
+- Visualizing systems of actors, including spawned and invoked machines
+- Warnings and errors for invalid machine definitions
+- Customizable state node, event, and transition colors
+- Rich text editing and content for state descriptions
+- Markdown generation
+- Event recording and playback
+- Machine versioning and diffing
+- Autolayout and smart layout controls
+- Teams and private machines/systems
+- _And so much more._
+
+## Join the public beta
+
+Excited to see what the Stately Editor can do? [Join the public beta by registering for free](https://stately.ai/registry?source=blog) and [create your first machine](https://stately.ai/editor?source=blog), or [discover other people’s machines](https://stately.ai/registry/discover?source=blog) and get inspired.
+
+Please [let us know](https://github.com/statelyai/editor-feedback/issues/new) if you have any feedback, feature requests or bug reports.
+
+
+ Try the Stately Editor beta and start modeling your app logic visually.
+
+
+## Next steps
+
+The editor is the initial state towards making the ultimate collaborative tool for visually editing even the most complex app logic. We’re excited to share the Stately Editor with the world, and we’re looking forward to hearing your reactions.
+
+[Join our Discord community](https://discord.gg/xstate) to get help, ask questions, and give us your feedback.
diff --git a/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/stately-editor-public-beta.png b/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/stately-editor-public-beta.png
new file mode 100644
index 000000000..0ace354de
Binary files /dev/null and b/fumadocs/content/blog/2022-2-6-stately-editor-public-beta/stately-editor-public-beta.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-contribute.png b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-contribute.png
new file mode 100644
index 000000000..094796924
Binary files /dev/null and b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-contribute.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-light-mode.png b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-light-mode.png
new file mode 100644
index 000000000..d9f35eb65
Binary files /dev/null and b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-light-mode.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-search.png b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-search.png
new file mode 100644
index 000000000..b88310cbc
Binary files /dev/null and b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-search.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-xstate.png b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-xstate.png
new file mode 100644
index 000000000..aa940ebb5
Binary files /dev/null and b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs-xstate.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs.png b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs.png
new file mode 100644
index 000000000..76553d096
Binary files /dev/null and b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/2023-01-19-docs.png differ
diff --git a/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/index.mdx b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/index.mdx
new file mode 100644
index 000000000..3208f8562
--- /dev/null
+++ b/fumadocs/content/blog/2023-01-19-introducing-the-stately-docs/index.mdx
@@ -0,0 +1,50 @@
+---
+title: 'Introducing the Stately docs'
+description: >-
+ The time has finally come; our new docs are ready to share with you all. If you’ve been following our office hours, you know I’ve been talking about these docs for a long time…
+tags: [studio, xstate, docs, search, light mode and dark mode, contribute]
+authors: [laura]
+image: /blog/2023-01-19-introducing-the-stately-docs.png
+slug: 2023-01-19-introducing-the-stately-docs
+date: 2023-01-19
+---
+
+The time has finally come; [our new docs](https://stately.ai/docs) are ready to share with you all. If you’ve been following our office hours, you know I’ve been talking about these docs for a long time. Thanks to Anders, who used [Docusaurus](https://docusaurus.io) to build us a rock solid easily-maintainable platform with search that actually works, and the whole team, who have contributed reviews, explainers, and examples to get these docs started.
+
+{/* truncate */}
+
+
+
+## Docs for the Stately Studio
+
+Our new docs have documentation for both the [Stately Studio](https://stately.ai/editor) and [XState](https://xstate.js.org). If you want to learn how to use the Studio without having to learn any code, we’ve got you covered with a [no-code introduction to state machines and statecharts](https://stately.ai/docs/state-machines-and-statecharts) and [step-by-step tutorials for different features in the Studio](https://stately.ai/docs/states/intro). We’ve also got a [modeling section](https://stately.ai/docs/descriptions) where we’ll add more guides to modeling your machines in the future.
+
+## Docs for XState
+
+If you’re a more experienced developer who wants to [jump straight into XState](https://stately.ai/docs/xstate/basics/what-is-a-statechart), we have docs that focus on the coding experience and cover all the concepts you need to know when working with XState. Shoutout to [Matt Pocock](https://totaltypescript.com), who used his vast knowledge of XState to kick these docs off when he was with us last year.
+
+
+
+## Useful search
+
+As I mentioned earlier, the search in the docs really works. Search is a problem for a lot of documentation across the web and something we were keen to improve over the previous XState docs. Anders worked hard to make the Algolia search work for us, and while the structure of our docs navigation is essential, we all know the search is where you’ll likely go first.
+
+
+
+## Dark mode
+
+One of our most-requested features for our docs was dark mode. Inspired by Nick’s light and dark mode designs for the Studio, I’ve given the docs light mode and dark modes that should hopefully be easy on the eyes and give you the best possible reading experience. Even our images respond to the light and dark modes!
+
+
+
+## Easy to contribute
+
+At the end of every page, we have an **Edit this page on GitHub** link, which takes you directly to that page in our docs’ GitHub repository. We appreciate any contributions that help our community learn XState and the Stately Studio. We have a short [Stately Guide to Writing Docs wiki](https://github.com/statelyai/docs/wiki) on [our repository](https://github.com/statelyai/docs/) with recommendations on structure and style. Read below if you want to suggest an improvement to our docs.
+
+
+
+## Still in beta
+
+These docs are still in beta, and we want your feedback! At Stately, we care a great deal about accessible and valuable education. Our docs are a living iterable product, and we want to continuously improve them to make them as valuable a resource as possible. Please [let us know in Discord](https://discord.gg/xstate) or [on Twitter](https://twitter.com/statelyai) if you have suggestions for improvements or where we could make the docs clearer or better for you. Use [our Canny documentation request board](https://feedback.stately.ai/docs) if you have a page, feature, or example request.
+
+We hope you like our new, improved docs, and we’re looking forward to hearing from you!
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2022-11-29-import-from-code-right-panel.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2022-11-29-import-from-code-right-panel.png
new file mode 100644
index 000000000..715f8393a
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2022-11-29-import-from-code-right-panel.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-19-docs.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-19-docs.png
new file mode 100644
index 000000000..76553d096
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-19-docs.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-machine-images.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-machine-images.png
new file mode 100644
index 000000000..92bb2ba24
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-machine-images.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-read-only.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-read-only.png
new file mode 100644
index 000000000..90b165c66
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-read-only.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-stately-streams.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-stately-streams.png
new file mode 100644
index 000000000..2d3bb58de
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-stately-streams.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-team-in-lisbon.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-team-in-lisbon.png
new file mode 100644
index 000000000..86035800a
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-team-in-lisbon.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-xstate-vscode.png b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-xstate-vscode.png
new file mode 100644
index 000000000..fc320fb7b
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/2023-01-20-xstate-vscode.png differ
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/index.mdx b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/index.mdx
new file mode 100644
index 000000000..f022e614b
--- /dev/null
+++ b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/index.mdx
@@ -0,0 +1,82 @@
+---
+title: 'What’s new for Stately in 2023'
+description: >-
+ Happy 2023! We thought we’d kick off the year with a reminder of all the features we’ve released since the Stately Studio 1.0 release in October.
+tags: [recovery, restore, offline, editor, studio]
+authors: [laura]
+image: /blog/2023-01-20-whats-new-in-2023.png
+slug: 2023-01-20-whats-new-in-2023
+date: 2023-01-20
+---
+
+Happy 2023! The Stately team is back from their [first off-site of the year in Lisbon](https://twitter.com/statelyai/status/1615394794251096065) and excited to get started on our plans for this year. We thought we’d kick off the year with a reminder of all the features we’ve released since the [Stately Studio 1.0 release](https://stately.ai/blog/2022-10-18-introducing-stately-studio-1-0) in October. It’s been just three months, and we already have so much to share.
+
+{/* truncate */}
+
+
+
+## Import from code
+
+[Import from code](https://stately.ai/blog/2022-11-29-import-from-code) was one of our most-requested features from users who had already created machines using XState or our Stately Viz. You can import an existing machine from JavaScript, TypeScript or JSON using the code panel in the editor’s right tool menu or the Machines list in the left drawer. Import from code works well with our existing export as code feature, where you can export your machine as JSON, JavaScript, and TypeScript using the code panel in the right tool menu.
+
+
+
+## Moving machines
+
+You can now quickly move machines between projects from the machines list menu. You can move your machines to your own projects or any projects you share with your teams. [Watch Kevin demo moving machines](https://www.youtube.com/watch?v=MpbbgOQ9WAI&t=709s) during our office hours in December last year.
+
+## Recovery mode
+
+Recovery mode is one of those features we hope you never have to use. We now try to detect any missing connectivity or backend failure on our end while you’re editing your machines. We will let you know if a connectivity issue or other failure happens and start saving your work locally on your device. The next time you visit the Studio from that same device, we compare what we saved with the machine from the server. If the versions are different, we will give you the option to restore the safe copy to a new machine.
+
+
+
+## Editor tutorials
+
+The editor now has several tutorials to help get you started, along with basic state machine and statechart concepts. You can access the tutorials from the blue question mark button in the lower right of the editor canvas. Most tutorials also have an accompanying short video for those who like to learn visually. You can [watch the playlist of all the tutorial videos on YouTube](https://www.youtube.com/watch?v=Aixi0e53qAE&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ).
+
+## XState VS Code update
+
+The XState VS Code extension has had a significant overhaul making it run much faster and giving it more accurate bi-directional editing between the code and visual editing modes.
+
+
+
+We’ve also added two much-requested features from our VS Code users; you can now use the XState VS Code without logging in, and use the extension entirely offline.
+
+## Learn more with the Stately streams
+
+If you want to learn more about using the Stately Studio and XState, check out [David and Farzad’s Stately streams](https://www.youtube.com/watch?v=25zL7hUOc1k&list=PLvWgkXBB3dd5UEJZCk4C3Wn1Ys8WRMDbi). These are a series of videos where David and Farzad work through modeling and coding features and best practices with state orchestration.
+
+
+
+## Docs
+
+[We’ve finally launched our new Stately docs](https://stately.ai/blog/2023-01-19-introducing-the-stately-docs). We now have docs for the Stately Studio and XState, with how-tos, tutorials, code examples, and much more. And yes, the new docs have dark mode!
+
+
+
+## Even more in the Studio
+
+We continue making improvements to the Studio to improve your experience. We’ve improved how we track features in the Studio, enhancing your privacy by keeping our users entirely anonymous in our analytics. We now support Safari and hope to work on the touchscreen experience in the future. And we’ve also introduced dedicated support for our Pro subscription users, which has a 100% success and speedy response record so far!
+
+### Machine metadata
+
+We now support metadata for states and events for those who want to include static data in their machines. You can add your metadata as JSON key-value pairs at the end of the State details panel. The Studio will recognize and format your values as strings, numbers, or booleans; all metadata is included when you export your machines to code.
+
+### Read-only mode
+
+At the end of last year, we added a read-only mode to the editor. Previously when viewing a read-only machine, you’d be able to edit the machine even though the editor wouldn’t save your changes. Now, when checking out a read-only machine, you won’t be able to edit the machine, and you’ll see a banner explaining why you can’t make changes.
+
+
+
+### Machine images
+
+You’ll love our new machine images if you’ve been looking for an easy way to share an overview of your machines on GitHub, Slack, Discord, or any other web platform. From the machines list menu, you can choose **Copy image URL** and use that URL anywhere you want to share your machine. The image will be live-updated when your machine is updated, so you don’t need to worry about your reference images going out of date.
+
+
+
+I’ve been using these images everywhere in our new docs, and you can see there that you can get light and dark mode versions of each image. The image URL copied to your clipboard will use the same mode you’re using in the Studio, but you can always change the mode by switching the .dark.png and .light.png suffixes in the URL. This feature is part of our work toward our GitHub integration, so look out for that coming soon!
+
+## Find out more
+
+If all of that isn’t enough for you, you can catch up with [David’s wrap-up of 2022](https://www.youtube.com/watch?v=-uoEro1jsBI&t=1916s) and a [preview of what’s to come from Stately in 2023](https://www.youtube.com/watch?v=-uoEro1jsBI&t=2140s). We can’t wait to share what we’re working on!
diff --git a/fumadocs/content/blog/2023-01-20-whats-new-in-2023/recovery_restore_machine.gif b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/recovery_restore_machine.gif
new file mode 100644
index 000000000..5483a2668
Binary files /dev/null and b/fumadocs/content/blog/2023-01-20-whats-new-in-2023/recovery_restore_machine.gif differ
diff --git a/fumadocs/content/blog/2023-02-06-github-import-machines/github-import-machine.gif b/fumadocs/content/blog/2023-02-06-github-import-machines/github-import-machine.gif
new file mode 100644
index 000000000..b2dde3115
Binary files /dev/null and b/fumadocs/content/blog/2023-02-06-github-import-machines/github-import-machine.gif differ
diff --git a/fumadocs/content/blog/2023-02-06-github-import-machines/github-install-integration.gif b/fumadocs/content/blog/2023-02-06-github-import-machines/github-install-integration.gif
new file mode 100644
index 000000000..d1fb4d8ab
Binary files /dev/null and b/fumadocs/content/blog/2023-02-06-github-import-machines/github-install-integration.gif differ
diff --git a/fumadocs/content/blog/2023-02-06-github-import-machines/github-save-machine.gif b/fumadocs/content/blog/2023-02-06-github-import-machines/github-save-machine.gif
new file mode 100644
index 000000000..a44b1e5b1
Binary files /dev/null and b/fumadocs/content/blog/2023-02-06-github-import-machines/github-save-machine.gif differ
diff --git a/fumadocs/content/blog/2023-02-06-github-import-machines/index.mdx b/fumadocs/content/blog/2023-02-06-github-import-machines/index.mdx
new file mode 100644
index 000000000..ebe2596d0
--- /dev/null
+++ b/fumadocs/content/blog/2023-02-06-github-import-machines/index.mdx
@@ -0,0 +1,52 @@
+---
+title: 'Import machines from GitHub'
+description: Learn about a new pro feature in the Stately Studio; importing machines from GitHub.
+tags: [stately, studio, integration, github]
+authors: [anders]
+image: /blog/2023-02-06-github-import-machines.png
+slug: 2023-02-06-github-import-machines
+date: 2023-02-06
+---
+
+Today we’re happy to introduce another pro feature for our Stately Studio subscribers; **import machines from GitHub**. With this feature, you can quickly visually machines in any of your GitHub repositories. You can even import the machines to the Studio and keep working on them here 🎉
+
+{/* truncate */}
+
+## How to
+
+Importing a machine is easy. You open a file containing one or more machines on GitHub. Next, you modify the URL, replace `.com` with `.stately.ai`, and the import will start.
+
+
+
+### Example
+
+If you have a machine hosted at GitHub: `https://github.com/username/repo/blob/main/apps/superMachine.ts`, you update the URL to `https://github.stately.ai/username/repo/blob/main/apps/superMachine.ts` and the import will start 🚀
+
+### Saving the machine
+
+You can easily save imported machines into a Studio project. After the import, you click the save button and choose a project for the machine.
+
+
+
+## Get started
+
+This feature requires the following prerequisites:
+
+- A **professional or enterprise** subscription to the Stately Studio.
+- You must install the GitHub integration app.
+
+When importing a machine, we help you with both of those tasks.
+
+Your GitHub token is stored on your own devices by design; we don’t save it in our database. This is to keep your data as safe as possible with the tradeoff being that you must accept the integration on the initial import for each device you use.
+
+See this example where the GitHub integration gets installed during the initial import.
+
+
+
+## Future work
+
+We have much more planned for integrating with GitHub; this is just the start. Let us know what you think about the machine import and what GitHub integration features you would like to see in the Stately Studio.
+
+### Bookmarklet bonus
+
+You can create a [bookmarklet](https://en.wikipedia.org/wiki/Bookmarklet) if you want to make it extra easy to import machines from GitHub. You do this by adding a new bookmark and setting the web address to `javascript:(function(){ location.href = 'https://github.stately.ai/' + window.location.pathname;})();`. Now you can click the bookmarklet whenever you want to import a machine. The bookmarklet will work on any GitHub file containing one or more machines.
diff --git a/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/2023-03-09-import-all-machines-from-github-repo-button.png b/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/2023-03-09-import-all-machines-from-github-repo-button.png
new file mode 100644
index 000000000..092a037a8
Binary files /dev/null and b/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/2023-03-09-import-all-machines-from-github-repo-button.png differ
diff --git a/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/index.mdx b/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/index.mdx
new file mode 100644
index 000000000..6a60f3d28
--- /dev/null
+++ b/fumadocs/content/blog/2023-03-09-import-all-machines-from-github-repo/index.mdx
@@ -0,0 +1,33 @@
+---
+title: 'Import all machines from your GitHub repo'
+description: Learn about a new pro feature in the Stately Studio; importing all your machines from a GitHub repo.
+tags: [stately, studio, integration, github]
+authors: [laura]
+image: /blog/2023-03-09-import-all-machines-from-github-repo.png
+slug: 2023-03-09-import-all-machines-from-github-repo
+date: 2023-03-09
+---
+
+Last month, Anders showed you how you could [import a machine from GitHub by changing the GitHub URL in the browser’s address bar](../../docs/assets/blog/2023-02-06-github-import-machines/index). We’ve added more to our GitHub integration. Our [Pro](https://stately.ai/pricing) users can now import all the machines from a repository into a Stately Studio project with the **Import from GitHub** button.
+
+{/* truncate */}
+
+
+
+If you’ve been working with state machines in XState or using the [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) to visualize your machines, this is a great way to import all the machines in your repo quickly. Once you’ve imported your machines into the Stately Studio, you can visualize and improve them in our Editor and easily share your machines with your team.
+
+## How to import all the machines from a GitHub repository
+
+1. Navigate to **My Projects** from the sidebar or the Stately menu.
+2. Use **Import from GitHub** to open the Import repo from GitHub modal.
+3. The GitHub integration will fetch all available repositories (public and private).
+4. Choose the repository from which you wish to import machines.
+5. The Studio will import your machines into a new private project using the same name as your repository and open your project in the Editor.
+
+Importing a machine with **Import from GitHub** or importing a machine with a GitHub URL will prompt you to **Allow integration** to give our GitHub integration access to your GitHub repositories. You will only be prompted if you have not yet given the GitHub integration access on your current device.
+
+Currently, the **Import from GitHub** feature imports all machines from the _default branch_ in your GitHub repository. If you want to import machines from a different branch or a pull request, we recommend [importing each machine from a GitHub URL](../../docs/assets/blog/2023-02-06-github-import-machines/index).
+
+## Try our Pro plan to import from GitHub
+
+Import from GitHub is one of the [Pro features](https://stately.ai/docs/studio-pro-plan) of the Stately Studio. We offer a **free trial** on the [Stately Studio Pro account](https://stately.ai/pricing) so you can explore how our Pro features work for you and your team today!
diff --git a/fumadocs/content/blog/2023-04-03-book-a-demo/index.mdx b/fumadocs/content/blog/2023-04-03-book-a-demo/index.mdx
new file mode 100644
index 000000000..55cd7aa04
--- /dev/null
+++ b/fumadocs/content/blog/2023-04-03-book-a-demo/index.mdx
@@ -0,0 +1,41 @@
+---
+title: 'Book a demo with the Stately team'
+description: Are you looking to take your team collaboration to the next level? Do you want to explore the features of Stately and XState to their fullest potential? Then book a live demo with the Stately team!
+tags: [stately, studio, demo, xstate]
+authors: [laura]
+image: /blog/2023-04-03-book-a-demo.png
+slug: 2023-04-03-book-a-demo
+date: 2023-04-03
+---
+
+Are you looking to take your team collaboration to the next level? Do you want to explore the features of Stately and XState to their fullest potential? Then book a live demo with the Stately team !
+
+{/* truncate */}
+
+Our team is excited to give you a walkthrough of the Stately Studio and explore your use cases. During the demo, you can get answers to all your questions about Stately and XState. Learning more about you and your team will help us develop future features that benefit your work.
+
+What can you expect from a live demo with the Stately team?
+
+## Walkthrough of the Stately Studio
+
+Get an in-depth tour of Stately’s visual editor and its features. Explore how the Stately Studio works for teams and learn how to build complex state machines quickly.
+
+## Explore your use cases
+
+Our team will help you explore the Stately Studio’s features by walking you through your use cases. We can help you understand how Stately can be used to solve your specific problems and how you can integrate it into your workflow.
+
+## Answer any questions you have about Stately and XState
+
+During the demo, our team will answer your questions about Stately and XState. Whether you’re curious about a particular feature or want to know how Stately can help you solve a specific problem, our team will provide the answers you need.
+
+## Follow-up demo designed for your team and use cases
+
+After the demo, our team can work with you to schedule a follow-up demo that is designed specifically for your team and use cases. We will consider your feedback from the initial demo and create a customized demo that meets your specific needs.
+
+
+ Use the link below to book your live demo. We look forward to helping you
+ build amazing applications with Stately and XState.
+
diff --git a/fumadocs/content/blog/2023-04-21-embed-url/index.mdx b/fumadocs/content/blog/2023-04-21-embed-url/index.mdx
new file mode 100644
index 000000000..7e03a3df6
--- /dev/null
+++ b/fumadocs/content/blog/2023-04-21-embed-url/index.mdx
@@ -0,0 +1,17 @@
+---
+title: Embed URL
+description: '🤫 We’re testing a new feature where you can embed machines.'
+authors: [laura]
+tags: [changelog, new, beta]
+date: 2023-04-21
+slug: 2023-04-21-embed-url
+image: /blog/2023-07-18-embed-url.png
+---
+
+🤫 We’re testing a new feature where you can embed machines.
+
+From the **Share** menu, there’s now a **Copy embed URL** button. You can use this URL in an `