Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,67 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline

<!-- changelog -->

## [Unreleased]

### Features:

* **Link Component**: Add React Link component for Phoenix LiveView navigation

The new `Link` component provides seamless navigation within Phoenix LiveView applications, supporting all LiveView navigation patterns:

- `href` - Traditional browser navigation (full page reload)
- `patch` - Patch current LiveView (calls handle_params)
- `navigate` - Navigate to different LiveView (same live_session)
- `replace` - Replace browser history instead of push

```jsx
import { Link } from "live_react";

<Link href="/external">External Link</Link>
<Link patch="/same?tab=new">Patch Current LV</Link>
<Link navigate="/other">Navigate to Other LV</Link>
<Link navigate="/path" replace={true}>Replace History</Link>
```

### 🔄 Upgrade Guide for Existing Projects

#### ✅ Automatic Updates (via `mix deps.update`)
These files are automatically updated when you update the `live_react` dependency:
- `assets/js/live_react/link.jsx` - New Link component
- `assets/js/live_react/index.mjs` - Updated exports
- `assets/js/live_react/index.d.mts` - New TypeScript definitions

#### 📝 Use Link in your React components

```javascript
// assets/react-components/index.js
import { Link } from "live_react";

export default {
// ... your existing components
Link, // Add this line
};
```

#### 🚀 Quick Start (No Manual Updates Needed)
You can start using the Link component immediately after updating:

```elixir
# In any LiveView template
<.react name="Link" href="/some-page">Click me</.react>
<.react name="Link" patch="/current?tab=new">Patch</.react>
<.react name="Link" navigate="/other-live-view">Navigate</.react>
```

#### 📊 What's Updated in live_react_examples
If you're using the examples app as reference, these files have been updated:
- Added `/link-demo` and `/link-usage` routes
- Updated navigation in layout
- Added demo LiveViews and React components
- Updated documentation

---

## [v1.0.1](https://github.com/mrdotb/live_react/compare/v1.0.1...v1.0.0) (2025-04-20)

### Bug Fixes:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ React inside Phoenix LiveView.
- 💀 **Dead View** Support
- 🐌 **Lazy-loading** React Components
- 🦥 **Slot** Interoperability
- 🔗 **Link Component** for LiveView Navigation
- 🚀 **Amazing DX** with Vite

## Resources
Expand Down
4 changes: 4 additions & 0 deletions assets/copy/react-components/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Simple } from "./simple";
import { LinkExample } from "./link-example";
import { Link } from "live_react";

export default {
Simple,
LinkExample,
Link,
};
40 changes: 40 additions & 0 deletions assets/copy/react-components/link-example.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import { Link } from "live_react";

export function LinkExample({}) {
return (
<div className="space-y-4 p-4">
<h2 className="text-xl font-bold">Link Component Examples</h2>

<div className="space-y-2">
<p>
<Link href="/external" className="text-blue-600 hover:underline">
External Link (full page reload)
</Link>
</p>

<p>
<Link patch="/same-liveview" className="text-green-600 hover:underline">
Patch Link (same LiveView, calls handle_params)
</Link>
</p>

<p>
<Link navigate="/other-liveview" className="text-purple-600 hover:underline">
Navigate Link (different LiveView, same session)
</Link>
</p>

<p>
<Link
navigate="/replace-history"
replace={true}
className="text-red-600 hover:underline"
>
Replace History (navigate with replace)
</Link>
</p>
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions assets/copy/react-components/link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Link } from "live_react";

// Re-export Link component for direct usage
export { Link };
15 changes: 15 additions & 0 deletions assets/js/live_react/index.d.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from "react";

export interface LiveProps {
pushEvent: (
event: string,
Expand All @@ -19,4 +21,17 @@ export interface LiveProps {
uploadTo: (target: string, name: string, files: FileList | File[]) => void;
}

export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
/** Uses traditional browser navigation to the new location. This means the whole page is reloaded. */
href?: string | null;
/** Patches the current LiveView. The handle_params callback will be invoked with minimum content sent over the wire. */
patch?: string | null;
/** Navigates to a LiveView. Only works between LiveViews in the same live_session. */
navigate?: string | null;
/** When using patch or navigate, should the browser's history be replaced with pushState? */
replace?: boolean;
children?: React.ReactNode;
}

export function useLiveReact(): LiveProps;
export function Link(props: LinkProps): React.ReactElement;
1 change: 1 addition & 0 deletions assets/js/live_react/index.mjs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { getHooks } from "./hooks";
export { useLiveReact } from "./context";
export { Link } from "./link.jsx";
39 changes: 39 additions & 0 deletions assets/js/live_react/link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useMemo } from "react";

/**
* Phoenix LiveView Link component for React
*
* Handles different types of navigation in Phoenix LiveView:
* - href: Traditional browser navigation (full page reload)
* - patch: Patches the current LiveView (calls handle_params)
* - navigate: Navigates to a different LiveView within the same live_session
* - replace: Whether to replace or push browser history
*/
export function Link({
href = null,
patch = null,
navigate = null,
replace = false,
children,
...attrs
}) {
const linkAttrs = useMemo(() => {
if (!patch && !navigate) {
return {
href: href || "#",
};
}

return {
href: (navigate ? navigate : patch) || "#",
"data-phx-link": navigate ? "redirect" : "patch",
"data-phx-link-state": replace ? "replace" : "push",
};
}, [href, patch, navigate, replace]);

return (
<a {...attrs} {...linkAttrs}>
{children}
</a>
);
}
2 changes: 2 additions & 0 deletions guides/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ The easiest way to get started with development is to clone live_react and run t
git clone https://github.com/mrdotb/live_react.git
cd live_react_examples
```

The examples include demonstrations of the Link component for LiveView navigation at `/link-demo` and `/link-usage`.
26 changes: 26 additions & 0 deletions guides/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,32 @@ children = [
<.react name="Simple" />
```

You can also use the built-in Link component for LiveView navigation:

```elixir
<!-- Use Link component directly in templates -->
<.react name="Link" href="/some-page">External Link</.react>
<.react name="Link" patch="/current-liveview?tab=new">Patch Link</.react>
<.react name="Link" navigate="/other-liveview">Navigate Link</.react>

<!-- Or import it in your React components -->
```

```javascript
import { Link } from "live_react";

function MyComponent() {
return (
<div>
<Link href="/external">Traditional Link</Link>
<Link patch="/same-lv?param=value">Patch Current LiveView</Link>
<Link navigate="/other-lv">Navigate to Other LiveView</Link>
<Link navigate="/replace" replace={true}>Replace History</Link>
</div>
);
}
```

14. (Optional) enable [stateful hot reload](https://twitter.com/jskalc/status/1788308446007132509) of phoenix LiveViews - it allows for stateful reload across the whole stack 🤯. Just adjust your `dev.exs` to look like this - add `notify` section and remove `live|components` from patterns.

```elixir
Expand Down
Loading