When you build an AI-powered app with Rork, connecting to OpenAI or Gemini APIs is the obvious first step. But requirements sometimes push in a different direction: users in areas without reliable internet, apps that shouldn't transmit sensitive user data, or budgets where API costs need to stay at zero.
That's where edge AI — AI running locally on a server within the same network, or eventually on-device — becomes relevant. This article covers the basics of connecting a Rork app to a local Ollama instance, with practical code for the patterns that matter most.
When Local AI Makes Sense for Your App
Not every app benefits from local AI. Some honest assessment:
Good fits:
- Journal or notes apps where privacy is the primary value proposition
- Outdoor apps (hiking, emergency guides) used in areas without cell coverage
- Healthcare or legal apps where data cannot leave the device or network
- Free apps where eliminating API costs changes the business model
Poor fits:
- Apps needing real-time news or current events
- Use cases requiring broad general knowledge or creativity
- Apps targeting low-spec devices as the primary audience
Connecting a Rork App to a Local Ollama Server
The most practical starting architecture: run Ollama on a PC in the same local network. The app connects over WiFi via HTTP — no internet required.
Ollama exposes a simple generate API that works well from React Native:
// Simple Ollama call from Rork app
const callLocalAI = async (userMessage) => {
const response = await fetch('http://192.168.1.10:11434/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gemma4:9b',
prompt: userMessage,
stream: false
})
});
const data = await response.json();
return data.response;
};Replace 192.168.1.10 with the local IP of the machine running Ollama.
Fallback Pattern for When Local Isn't Available
Users take their phones outside the home network. A graceful fallback to cloud API keeps the app functional:
const callAI = async (userMessage) => {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);
const response = await fetch('http://192.168.1.10:11434/api/generate', {
method: 'POST',
signal: controller.signal,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gemma4:9b',
prompt: userMessage,
stream: false
})
});
clearTimeout(timeoutId);
const data = await response.json();
return { text: data.response, source: 'local' };
} catch (localError) {
// Fallback to cloud API
return await callCloudAI(userMessage);
}
};The source: 'local' flag lets you show the user a privacy indicator when their data stayed local.
A Basic Chat Screen Implementation
// ChatScreen.js — integrate into Rork-generated app
import React, { useState } from 'react';
import { View, TextInput, FlatList, TouchableOpacity, Text } from 'react-native';
export default function ChatScreen() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const sendMessage = async () => {
if (!input.trim() || loading) return;
const userMessage = { id: Date.now(), role: 'user', text: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setLoading(true);
try {
const response = await callAI(input);
const aiMessage = {
id: Date.now() + 1,
role: 'assistant',
text: response.text,
isLocal: response.source === 'local'
};
setMessages(prev => [...prev, aiMessage]);
} finally {
setLoading(false);
}
};
return (
<View style={{ flex: 1, padding: 16 }}>
<FlatList
data={messages}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<View style={{
alignSelf: item.role === 'user' ? 'flex-end' : 'flex-start',
backgroundColor: item.role === 'user' ? '#007AFF' : '#E5E5E5',
borderRadius: 12,
padding: 12,
marginBottom: 8,
maxWidth: '80%'
}}>
<Text style={{ color: item.role === 'user' ? 'white' : 'black' }}>
{item.text}
</Text>
{item.isLocal && (
<Text style={{ fontSize: 10, color: '#888', marginTop: 4 }}>
🔒 Processed locally
</Text>
)}
</View>
)}
/>
<View style={{ flexDirection: 'row', marginTop: 8 }}>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Type a message..."
style={{ flex: 1, borderWidth: 1, borderRadius: 8, padding: 8 }}
/>
<TouchableOpacity
onPress={sendMessage}
disabled={loading}
style={{ marginLeft: 8, backgroundColor: '#007AFF', borderRadius: 8, padding: 8 }}
>
<Text style={{ color: 'white' }}>Send</Text>
</TouchableOpacity>
</View>
</View>
);
}Getting Started
The minimum viable test: start Ollama on your development machine, note its local IP address, update the endpoint in the code above, and build a Rork app that makes a simple API call. The mechanics are simpler than most AI integrations.
From there, the interesting questions become architecture questions: how do you distribute the local server setup to users? How do you handle model updates? How do you build truly on-device inference?
The premium article covers those questions in depth — on-device model integration, model distribution strategies, background inference, and building a fully offline-capable app.