All Posts(7)

Page 2 of 2
The future

uv - Blazingly Fast 😏 🚀

A Python's Package and Project Manager written in Rust

uv is an extremely fast package and project manager for Python, written in Rust. I hear you mumbling, another package manager for Python? Bro, this time is different, hear me out, uv really is the future. In this post we’ll spend some minutes learning how to use it.

What can I do with it?

Lots of things, but from the top of my head:

Installation

There are plenty of ways to get this puppy up and running in our machine. The first one, if you’re lucky enough to be on Linux or macOS:

Terminal window
$ curl -LsSf https://astral.sh/uv/install.sh | sh

After the installation, we have to restart our shell (just type exit in your terminal). That’s because the installer added uv to our PATH.

NOTE

If you’re in Windows 🤢:

Terminal window
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

For Christ’s sake, you can even install it with pip!

Terminal window
pip install uv

Anyways, once you’ve installed it, and as a sanity test, run:

Terminal window
$ uv
An extremely fast Python package manager.
Usage: uv [OPTIONS] <COMMAND>

TIP

Check the docs for uninstallation if you need to.

Installing Python Versions

uv can also install and manage Python versions:

Terminal window
$ uv python install

That would get us the latest version, but if you want to install a specific one, just say it:

Terminal window
$ uv python install 3.12

NOTE

If you run the command above (regardless if you’re in a project folder), it will install Python in ~/.local/share/uv/python/cpython-3.13.3-macos-aar, and create a symlink to it in ~/.local/bin/python3.13.

To see a list of the available and installed Python versions:

Terminal window
$ uv python list

IMPORTANT

We don’t need to install Python to get started; If Python is already installed on your system, uv will detect and use it.

Projects

uv supports managing Python projects, which define their dependencies in a pyproject.toml file. Like in other languages, we can create a new Python project using the uv init command:

Terminal window
uv init my-proj

That will create the following project structure within the my-proj folder:

my-proj/
.
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

TIP

If you already have a project folder, let’s say you already have the my-proj folder, you can run just uv init within the folder.

The main.py file contains a simple “Hello world” program. Try it out with uv run:

Terminal window
$ cd my-proj
$ uv run main.py
Using CPython 3.13.2 interpreter at: /usr/bin/python3.13
Creating virtual environment at: .venv
Hello from my-proj!

Now, if we list the content of our project:

Terminal window
$ ls -a
.
├── .git
├── .gitignore
├── .venv
│   ├── bin
│   ├── lib
│   └── pyvenv.cfg
├── .python-version
├── README.md
├── main.py
├── pyproject.toml
└── uv.lock

That’s right, uv has created a virtual environment (in .venv folder), and initialized a git repo for us. From a project point of view, the pyproject.toml contains our project’s metadata (sort of the package.json in Node.js):

[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
dependencies = []

Managing Dependencies

Managing dependencies is easy. Let’s say we want to install marimo:

uv add marimo

Marimo includes a tutorial, so let’s run it, to verify it’s installed:

uv run marimo tutorial intro

WARNING

If you try to run:

marimo tutorial
zsh: command not found: marimo

Your shell will complain! That’s because when we run uv add marimo, uv installs marimo into a virtual environment (under the .venv folder). This environment is isolated, meaning the marimo executable is only available when the virtual environment is activated or when you explicitly use uv run to access it.

I hope that was enough to get you started; feel free to check the official docs, they’re awesome.

Bad coffee

React Router 7

How to use the Framework mode

React Router is a multi-strategy router for React, which means it can be used in different ways depending on the needs of your application. In this guide, we’ll explore the framework mode, which is a new way to use React Router that allows you to define your routes in a more declarative way.

Generating a new React project

The framework mode is the one that offers the most features (check comparison table), hence it has a few dependencies than the other modes. For that reason, setting it up manually it’s a bit tricky. A better idea is to generate a new project using the following command:

Terminal window
npx create-react-router@latest my-react-router-app

Where my-react-router-app is the name of our project. This command will generate a new React project with the all the necessary dependencies to use React Router in framework mode.

NOTE

Later in this guide, and as an exercise, we’ll see how to add the framework mode to an already existing React project.

Once we have our project created, we can navigate to the root folder and start the development server:

Terminal window
cd my-react-router-app
npm run dev

Important Files

If we open the project in our code editor, we should see a bunch of files and folders, but the most important ones are inside the app folder:

  • app/root.tsx: This is the entry point of our application, from where we export:
    • The Route.LinksFunction.
    • The Layout component.
    • The App component.
    • An ErrorBoundary component.
  • app/routes.ts: This is where we define our routes.

Defining Routes

Routes must be defined in the app/routes.ts file. For example, let’s define a simple route that renders a route module named home.tsx when the URL is /:

app/routes.ts
import { type RouteConfig, index } from '@react-router/dev/routes'
export default [
route('/', 'routes/home.tsx')
] satisfies RouteConfig

The route function is known as a route matcher; this route matcher takes two arguments:

  • A URL pattern to match the URL, in this case ’/’.
  • A file path to the route module that will be rendered when the URL matches the pattern.

Since having a route for the home page is a common use case, React Router provides a helper function called index that does the same as route('/', 'routes/home.tsx'):

app/routes.ts
import { type RouteConfig, index } from '@react-router/dev/routes'
export default [
route('/', 'routes/home.tsx')
index('routes/home.tsx')
] satisfies RouteConfig

These are the contents of routes/home.tsx:

routes/home.tsx
export default function Index() {
return <h1>You are at Index</h1>
}

IMPORTANT

Note that routes/home.tsx is a route module and not just a React component. We have to export the React component as default, otherwise we’ll get error:

You made a GET request to / but did not provide a `loader` for route "routes/home", so there is no way to handle the request.

Yeah, the error is a bit misleading, because we’re not loading any data in our componet; again, it happens because we didn’t export the component as default.

Adding a Loader

Let’s define data loading for our route module. There are two ways to do this:

Client Data Loading

Let’s add a clientLoader function to fetch a list of TODOs from the JSONPlaceholder API. We’ll render the list of TODOs in the home.tsx route module:

routes/home.tsx
import type { Route } from '../+types/root'
interface Todo {
userId: number
id: number
title: string
completed: boolean
} // Better move this type to a separate file (types/todo.ts) and import it here.
export async function clientLoader() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos')
if (!response.ok) {
throw new Error('Failed to fetch todos')
}
return response.json()
}
export default function Home({ loaderData }: Route.ComponentProps) {
return (
<main>
<h1>You are at Index</h1>
<h2>Todos</h2>
<ul>
{(loaderData ?? []).map((todo: Todo) => (
<li key={todo.id} className="py-4">
<h3>{todo.title}</h3>
<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
</li>
))}
</ul>
</main>
)
}

Yeah, I know, it’s a lot of code for rendering just a TODO list!, but let’s break it down:

  • We define a Todo type that represents the structure of the data we’re going to fetch.
  • Note that clientLoader runs on the client side, like a traditional SPA; open the network tab to verify the request.
  • In the React component, we destructure the loaderData prop to access the data fetched by the clientLoader function. Then we render the list of TODOs.

TIP

When testing this code, the browser’s console will show a recommendation to use the HydrateFallback, so I added:

// HydrateFallback is rendered while the client loader is running
export function HydrateFallback() {
return <div>Loading...</div>;
}

Nice! So no need of defining React loading states.

Server Data Loading

Server Data Loading works the same way as the clientLoader, but it runs on the server side. Let’s add a loader function to fetch the individual TODOs:

routes/todo.tsx
import type { Route } from '../+types/root'
import { Link } from 'react-router'
import type { Todo } from 'types/todo'
export async function loader({ params }: { params: { id: string } }) {
const { id } = params
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
if (!response.ok) {
throw new Error('Failed to fetch todos!')
}
return await response.json()
}
export default function Todo({ loaderData }: Route.ComponentProps) {
const todo = loaderData ? (loaderData as Todo) : null
return (
<main>
<Link to="/">Back Home</Link>
{todo && (
<div key={todo.id}>
<h3>{todo.title}</h3>
<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
</div>
)}
</main>
)
}

TIP

Feel free to wrap the TODOs in routes/home.tsx in a Link component to navigate to the individual TODO route with a click.

The loader function runs on the server, so you won’t be able to see the network request in the browser’s console. We get good old HTML from the backend, which you can verify it by disabling JavaScript in the browser.

IMPORTANT

Remember to add a route in app/routes.ts to render the todo.tsx route module:

app/routes.ts
import { type RouteConfig, index, route } from '@react-router/dev/routes'
export default [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx')
] satisfies RouteConfig

In the code above, we’re using a dynamic segment in the first argument of the route function (todo:id). We end up with a dynamic route matcher that matches the URL /todo/:id and renders the todo.tsx route module. The :id part is an argument that we’ll be used in the loader function to render the TODO with that id.

Adding a Layout Route

It’s pretty common to have a layout component shared amongst several routes in our application. As you can see, both our route modules have a main element repeated (in real life scenarios we’d have way more than that). We can define a layout route in the app/root.tsx file:

app/root.tsx
import { Layout } from '@react-router/dev/routes'
export default [
layout("./components/layout.tsx", [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx')
]),
]

An this is the content of components/layout.tsx:

components/layout.tsx
import { Outlet } from 'react-router'
export default function Layout() {
return (
<div>
<header>
<h1>TODO list app</h1>
</header>
<main>
<Outlet />
</main>
</div>
)
}

So the way this works is that the Layout component is rendered for all the nested routes, wrapping them, and the Outlet component is used to render the child routes.

IMPORTANT

Every route in routes.ts is nested inside the special root route, defined in the app/root.tsx module (Remember the Layout component we mentioned at the beginning?).

Nested routes

We can also define nested routes in our application. Let’s say we want to have a

Adding a 404 Route

This one is really simple, we just have to add a route matcher that matches any URL:

app/routes.ts
import { type RouteConfig, index, route } from '@react-router/dev/routes'
export default [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx'),
route('*', 'routes/not-found.tsx')
] satisfies RouteConfig

And the content of routes/not-found.tsx:

routes/not-found.tsx
export default function NotFound() {
return <h1>404 - Not Found</h1>
}
Cool image

React with Deno

How to start a React project in Deno!

So you’ve been hearing about a lot of good things about Deno, want to try it out but don’t know where to start? Don’t worry, got you covered son! How about we set up a React project using the Jurassic runtime and Vite.js? This guide will also show you how to install Deno.

NOTE

Deno is a simple, modern, and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust. It’s a great alternative to Node.js, and promises to be more secure and faster.

Installing Deno

The Deno installation in macOS/Linux, is very simple, you just need to run the following command:

Terminal window
curl -fsSL https://deno.land/install.sh | sh

TIP

In macOS, you can also install Deno using Homebrew, just run brew install deno. Or using your package manager in Linux.

For Windows, you can use PowerShell:

Terminal window
irm https://deno.land/install.ps1 | iex

WARNING

For the time you’re reading this, the installation process may have changed. Please refer to the official Deno installation guide for the most up-to-date instructions.

Starting a new React project

To start a new React project using Deno, we’ll use Vite.js, a build tool that aims to provide a faster and leaner development experience for modern web projects. To create a new React project, run the following command:

Terminal window
deno run -RWE npm:create-vite-extra@latest

The npm: prefix is used to indicate that the create-vite-extra script (that will help you create a new Vite project with extra templates and options) should be fetched from the npm registry. This allows Deno to run npm packages directly without needing a separate package manager like npm or yarn.

NOTE

The -RWE flags in the deno run command are used to grant specific permissions to the script being executed. Here’s what each flag stands for:

  • -R (short for --allow-read) grants the script permission to read files and directories.
  • -W (short for --allow-write) grants the script permission to write to files and directories.
  • -E (short for --allow-env) grants the script permission to access environment variables.

Remember when we mentioned that Deno is more secure? This is one example.

This command will start a prompt asking you for the project name, template, and other options. Choose the React template and follow the instructions. When it finishes, you’ll see the following message:

Scaffolding project in /home/bob/react-deno...
Done. Now run:
cd react-deno
deno task dev

That’s it, not much to be done. Just go to the project folder and run deno task dev to start the development server. You can now start coding your new React project using Deno!

Installing packages

In the previous section we’ve seen how to create a new React project using Vite.js. Now, let’s see how to install new packages in your project. For example, let’s install React Router, a popular routing library for React:

Terminal window
deno add npm:react-router@latest

TIP

Read our other post about React Router 7 to learn more about how to use it.

Formatting Code

Deno includes a built-in code formatter called deno fmt that can be used to format your code. To format all the files in your project, run the following command:

Terminal window
deno fmt

But running a command each time you want to format your code can be cumbersome. To make it easier, we’ll make it so that when we use a shortcut, our code is automatically formatted (it can be also be configured on save).

Installing the Deno VSCode Extension

We just have to open the Extensions panel in VSCode, and search for the Deno extension.

Initialize Workspace Configuration

Once the Deno extension has been installed, you can initialize the workspace configuration by running the Deno: Init command from the command palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows/Linux).

Deno Init

A file called .vscode/settings.json will be created in your workspace with the following configuration:

{
"deno.enable": true,
}

That enables Deno in your workspace. Now, to configure the code formatting, we can add the following to the file above:

"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[javascript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}

TIP

Optionally, if you want to format the code on save, add the following:

"editor.formatOnSave": true

Also, we can configure the formatting style in the deno.json file:

"fmt": {
"options": {
"useTabs": false,
"lineWidth": 80,
"indentWidth": 2,
"singleQuote": true,
"proseWrap": "always",
"semiColons": false
}
}

WARNING

Be careful with the formatting settings, if you are used to the ones used by Prettier, you may have to adjust them; e.g. "semi": false doesn’t work in Deno.