Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

FastUI

CIpypiversionslicense

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

fromdatetimeimportdatefromfastapiimportFastAPI, HTTPExceptionfromfastapi.responsesimportHTMLResponsefromfastuiimportFastUI, AnyComponent, prebuilt_html, componentsascfromfastui.components.displayimportDisplayMode, DisplayLookupfromfastui.eventsimportGoToEvent, BackEventfrompydanticimportBaseModel, Fieldapp=FastAPI()
classUser(BaseModel):
id: intname: strdob: date=Field(title='Date of Birth')
# define some usersusers= [
User(id=1, name='John', dob=date(1990, 1, 1)),
User(id=2, name='Jack', dob=date(1991, 1, 1)),
User(id=3, name='Jill', dob=date(1992, 1, 1)),
User(id=4, name='Jane', dob=date(1993, 1, 1)),
]
@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)defusers_table() ->list[AnyComponent]:
""" Show a table of four users, `/api` is the endpoint the frontend will connect to when a user visits `/` to fetch components to render. """return [
c.Page( # Page provides a basic container for componentscomponents=[
c.Heading(text='Users', level=2), # renders `<h2>Users</h2>`c.Table(
data=users,
# define two columns for the tablecolumns=[
# the first is the users, name rendered as a link to their profileDisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
# the second is the date of birth, rendered as a dateDisplayLookup(field='dob', mode=DisplayMode.date),
],
),
]
),
]
@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)defuser_profile(user_id: int) ->list[AnyComponent]:
""" User profile page, the frontend will fetch this when the user visits `/user/{id}/`. """try:
user=next(uforuinusersifu.id==user_id)
exceptStopIteration:
raiseHTTPException(status_code=404, detail="User not found")
return [
c.Page(
components=[
c.Heading(text=user.name, level=2),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(data=user),
]
),
]
@app.get('/{path:path}')asyncdefhtml_landing() ->HTMLResponse:
"""Simple HTML page which serves the React app, comes last as it matches all paths."""returnHTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines a rich set of components.

All components are listed in the demo app.

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original PhD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application you're building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the puppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • You only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • Deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #18)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • Implementing a web frontend using another JS framework like Vue — lots of work, limited value IMHO
  • Implementing a web frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • Implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • Implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

About

Build better UIs faster.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages